[libc-commits] [libc] [libc] Add getgrent, setgrent, and endgrent entrypoints (PR #224372)

via libc-commits libc-commits at lists.llvm.org
Thu Sep 17 11:14:19 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Jeff Bailey (kaladron)

<details>
<summary>Changes</summary>

Implement the POSIX group database iteration entrypoints getgrent, setgrent, and endgrent on top of grp_utils and FlatFileDatabase.

Non-reentrant calls read into a process-global DynamicBuffer that grows to the high-water mark of the largest group record seen. endgrent closes the file stream without freeing the buffer so pointers returned prior to endgrent remain valid until the next non-reentrant call.

* Add getgrent, setgrent, and endgrent entrypoints
* Define getgrent, setgrent, and endgrent in include/grp.yaml
* Expose entrypoints across Linux target architectures
* Add hermetic integration tests in test/src/grp/getgrent_test.cpp

Assisted-by: Automated tooling, human reviewed.

---

Patch is 20.46 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/224372.diff


16 Files Affected:

- (modified) libc/config/linux/aarch64/entrypoints.txt (+5) 
- (modified) libc/config/linux/arm/entrypoints.txt (+5) 
- (modified) libc/config/linux/i386/entrypoints.txt (+5) 
- (modified) libc/config/linux/riscv/entrypoints.txt (+5) 
- (modified) libc/config/linux/x86_64/entrypoints.txt (+5) 
- (modified) libc/include/grp.yaml (+16-1) 
- (modified) libc/src/grp/CMakeLists.txt (+43) 
- (added) libc/src/grp/endgrent.cpp (+27) 
- (added) libc/src/grp/endgrent.h (+26) 
- (added) libc/src/grp/getgrent.cpp (+30) 
- (added) libc/src/grp/getgrent.h (+27) 
- (added) libc/src/grp/setgrent.cpp (+27) 
- (added) libc/src/grp/setgrent.h (+26) 
- (modified) libc/test/src/grp/CMakeLists.txt (+26) 
- (added) libc/test/src/grp/getgrent_test.cpp (+208) 
- (added) libc/test/src/grp/grp_test_utils.h (+62) 


``````````diff
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 1a05ed55365b4..4de8ce85b0569 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -44,6 +44,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.fcntl.posix_fadvise
     libc.src.fcntl.posix_fadvise64
 
+    # grp.h entrypoints
+    libc.src.grp.endgrent
+    libc.src.grp.getgrent
+    libc.src.grp.setgrent
+
     # poll.h entrypoints
     libc.src.poll.poll
 
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index e32430cac7c8c..f4347331d0b31 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -30,6 +30,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     # errno.h entrypoints
     libc.src.errno.errno
 
+    # grp.h entrypoints
+    libc.src.grp.endgrent
+    libc.src.grp.getgrent
+    libc.src.grp.setgrent
+
     # poll.h entrypoints
     libc.src.poll.poll
 
diff --git a/libc/config/linux/i386/entrypoints.txt b/libc/config/linux/i386/entrypoints.txt
index 4766ea1522cae..f2b7dcca42c08 100644
--- a/libc/config/linux/i386/entrypoints.txt
+++ b/libc/config/linux/i386/entrypoints.txt
@@ -2,6 +2,11 @@ set(TARGET_LIBC_ENTRYPOINTS
   # errno.h entrypoints
   libc.src.errno.errno
 
+  # grp.h entrypoints
+  libc.src.grp.endgrent
+  libc.src.grp.getgrent
+  libc.src.grp.setgrent
+
   # pwd.h entrypoints
   libc.src.pwd.endpwent
   libc.src.pwd.getpwent
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index ba3600966fabf..889b4c5c79dbb 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -55,6 +55,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.fcntl.posix_fadvise
     libc.src.fcntl.posix_fadvise64
 
+    # grp.h entrypoints
+    libc.src.grp.endgrent
+    libc.src.grp.getgrent
+    libc.src.grp.setgrent
+
     # net/if.h entrypoints
     libc.src.net.if_indextoname
     libc.src.net.if_nametoindex
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index a32e83236e277..8e748287a2469 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -55,6 +55,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.fcntl.posix_fadvise
     libc.src.fcntl.posix_fadvise64
 
+    # grp.h entrypoints
+    libc.src.grp.endgrent
+    libc.src.grp.getgrent
+    libc.src.grp.setgrent
+
     # net/if.h entrypoints
     libc.src.net.if_indextoname
     libc.src.net.if_nametoindex
diff --git a/libc/include/grp.yaml b/libc/include/grp.yaml
index 015d9c46e195f..419c1212fba3c 100644
--- a/libc/include/grp.yaml
+++ b/libc/include/grp.yaml
@@ -7,4 +7,19 @@ types:
   - type_name: size_t
 enums: []
 objects: []
-functions: []
+functions:
+  - name: endgrent
+    standards:
+      - posix
+    return_type: void
+    arguments: []
+  - name: getgrent
+    standards:
+      - posix
+    return_type: struct group *
+    arguments: []
+  - name: setgrent
+    standards:
+      - posix
+    return_type: void
+    arguments: []
diff --git a/libc/src/grp/CMakeLists.txt b/libc/src/grp/CMakeLists.txt
index 9e460e376a14f..67f3b900f55d8 100644
--- a/libc/src/grp/CMakeLists.txt
+++ b/libc/src/grp/CMakeLists.txt
@@ -3,6 +3,49 @@ if(NOT TARGET libc.src.__support.File.file OR
   return()
 endif()
 
+add_entrypoint_object(
+  endgrent
+  SRCS
+    endgrent.cpp
+  HDRS
+    endgrent.h
+  DEPENDS
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.errno.errno
+    .grp_utils
+)
+
+add_entrypoint_object(
+  getgrent
+  SRCS
+    getgrent.cpp
+  HDRS
+    getgrent.h
+  DEPENDS
+    libc.hdr.types.struct_group
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.errno.errno
+    .grp_utils
+)
+
+add_entrypoint_object(
+  setgrent
+  SRCS
+    setgrent.cpp
+  HDRS
+    setgrent.h
+  DEPENDS
+    libc.src.__support.common
+    libc.src.__support.libc_errno
+    libc.src.__support.macros.config
+    libc.src.errno.errno
+    .grp_utils
+)
+
 add_object_library(
   grp_utils
   HDRS
diff --git a/libc/src/grp/endgrent.cpp b/libc/src/grp/endgrent.cpp
new file mode 100644
index 0000000000000..0af51d7970b8b
--- /dev/null
+++ b/libc/src/grp/endgrent.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of endgrent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/grp/endgrent.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/grp/grp_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, endgrent, ()) {
+  auto res = grp::close();
+  if (!res.has_value())
+    libc_errno = res.error();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/grp/endgrent.h b/libc/src/grp/endgrent.h
new file mode 100644
index 0000000000000..920beec028453
--- /dev/null
+++ b/libc/src/grp/endgrent.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Header file for endgrent function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GRP_ENDGRENT_H
+#define LLVM_LIBC_SRC_GRP_ENDGRENT_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Closes the group database stream.
+void endgrent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_GRP_ENDGRENT_H
diff --git a/libc/src/grp/getgrent.cpp b/libc/src/grp/getgrent.cpp
new file mode 100644
index 0000000000000..b17b1a4b66a27
--- /dev/null
+++ b/libc/src/grp/getgrent.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of getgrent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/grp/getgrent.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/grp/grp_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(struct group *, getgrent, ()) {
+  auto res = grp::read_next();
+  if (!res.has_value()) {
+    libc_errno = res.error();
+    return nullptr;
+  }
+  return res.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/grp/getgrent.h b/libc/src/grp/getgrent.h
new file mode 100644
index 0000000000000..64b54920c011d
--- /dev/null
+++ b/libc/src/grp/getgrent.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Header file for getgrent function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GRP_GETGRENT_H
+#define LLVM_LIBC_SRC_GRP_GETGRENT_H
+
+#include "hdr/types/struct_group.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Reads the next entry from the group database.
+struct group *getgrent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_GRP_GETGRENT_H
diff --git a/libc/src/grp/setgrent.cpp b/libc/src/grp/setgrent.cpp
new file mode 100644
index 0000000000000..196b24d569482
--- /dev/null
+++ b/libc/src/grp/setgrent.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of setgrent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/grp/setgrent.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/grp/grp_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, setgrent, ()) {
+  auto res = grp::open();
+  if (!res.has_value())
+    libc_errno = res.error();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/grp/setgrent.h b/libc/src/grp/setgrent.h
new file mode 100644
index 0000000000000..0a7290e91683c
--- /dev/null
+++ b/libc/src/grp/setgrent.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Header file for setgrent function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GRP_SETGRENT_H
+#define LLVM_LIBC_SRC_GRP_SETGRENT_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Rewinds the group database stream to the beginning.
+void setgrent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_GRP_SETGRENT_H
diff --git a/libc/test/src/grp/CMakeLists.txt b/libc/test/src/grp/CMakeLists.txt
index dbc1ac765455b..93a97482acca8 100644
--- a/libc/test/src/grp/CMakeLists.txt
+++ b/libc/test/src/grp/CMakeLists.txt
@@ -19,3 +19,29 @@ add_libc_test(
     libc.src.__support.pwd.flat_file_db
     libc.src.grp.grp_utils
 )
+
+add_libc_test(
+  getgrent_test
+  SUITE
+    libc_grp_unittests
+  HDRS
+    grp_test_utils.h
+  SRCS
+    getgrent_test.cpp
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.types.gid_t
+    libc.hdr.types.size_t
+    libc.hdr.types.struct_group
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+    libc.src.__support.libc_errno
+    libc.src.errno.errno
+    libc.src.grp.endgrent
+    libc.src.grp.getgrent
+    libc.src.grp.grp_utils
+    libc.src.grp.setgrent
+    libc.src.stdio.remove
+    libc.src.string.string_utils
+    libc.test.UnitTest.ErrnoCheckingTest
+)
diff --git a/libc/test/src/grp/getgrent_test.cpp b/libc/test/src/grp/getgrent_test.cpp
new file mode 100644
index 0000000000000..f7430c284ac5e
--- /dev/null
+++ b/libc/test/src/grp/getgrent_test.cpp
@@ -0,0 +1,208 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for getgrent, setgrent, and endgrent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/gid_t.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/struct_group.h"
+#include "src/__support/libc_errno.h"
+#include "src/grp/endgrent.h"
+#include "src/grp/getgrent.h"
+#include "src/grp/grp_utils.h"
+#include "src/grp/setgrent.h"
+#include "test/UnitTest/Test.h"
+#include "test/src/grp/grp_test_utils.h"
+
+using LIBC_NAMESPACE::libc_errno;
+
+TEST_F(LlvmLibcGrpTest, GetGrentTestSuccess) {
+  constexpr char CONTENT[] = "root:x:0:root\n"
+                             "bin:x:1:bin,daemon\n"
+                             "wheel:x:10:root,admin,user1\n";
+  ScopedGroupFile test_file(libc_make_test_file_path("getgrent_success.test"),
+                            CONTENT);
+
+  struct group *grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "root");
+  EXPECT_STREQ(grp->gr_passwd, "x");
+  EXPECT_EQ(grp->gr_gid, static_cast<gid_t>(0));
+  ASSERT_NE(grp->gr_mem, nullptr);
+  EXPECT_STREQ(grp->gr_mem[0], "root");
+  EXPECT_EQ(grp->gr_mem[1], nullptr);
+
+  grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "bin");
+  EXPECT_STREQ(grp->gr_passwd, "x");
+  EXPECT_EQ(grp->gr_gid, static_cast<gid_t>(1));
+  ASSERT_NE(grp->gr_mem, nullptr);
+  EXPECT_STREQ(grp->gr_mem[0], "bin");
+  EXPECT_STREQ(grp->gr_mem[1], "daemon");
+  EXPECT_EQ(grp->gr_mem[2], nullptr);
+
+  grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "wheel");
+  EXPECT_STREQ(grp->gr_passwd, "x");
+  EXPECT_EQ(grp->gr_gid, static_cast<gid_t>(10));
+  ASSERT_NE(grp->gr_mem, nullptr);
+  EXPECT_STREQ(grp->gr_mem[0], "root");
+  EXPECT_STREQ(grp->gr_mem[1], "admin");
+  EXPECT_STREQ(grp->gr_mem[2], "user1");
+  EXPECT_EQ(grp->gr_mem[3], nullptr);
+
+  // At EOF, getgrent returns nullptr without altering errno.
+  libc_errno = 0;
+  grp = LIBC_NAMESPACE::getgrent();
+  EXPECT_EQ(grp, nullptr);
+  ASSERT_ERRNO_SUCCESS();
+
+  LIBC_NAMESPACE::endgrent();
+}
+
+TEST_F(LlvmLibcGrpTest, GetGrentTestFailure) {
+  constexpr char CONTENT[] = "malformed_line_without_colons\n";
+  ScopedGroupFile test_file(libc_make_test_file_path("getgrent_failure.test"),
+                            CONTENT);
+
+  struct group *grp = LIBC_NAMESPACE::getgrent();
+  EXPECT_EQ(grp, nullptr);
+  ASSERT_ERRNO_EQ(EINVAL);
+
+  LIBC_NAMESPACE::endgrent();
+}
+
+TEST_F(LlvmLibcGrpTest, SetGrentTestHermetic) {
+  constexpr char CONTENT[] = "root:x:0:root\n"
+                             "bin:x:1:bin\n";
+  ScopedGroupFile test_file(libc_make_test_file_path("setgrent_hermetic.test"),
+                            CONTENT);
+
+  struct group *grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "root");
+
+  // Rewind the group database stream to the beginning.
+  LIBC_NAMESPACE::setgrent();
+
+  grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "root");
+
+  LIBC_NAMESPACE::endgrent();
+}
+
+TEST_F(LlvmLibcGrpTest, ReopenAfterEndgrent) {
+  constexpr char CONTENT[] = "root:x:0:root\n"
+                             "bin:x:1:bin\n";
+  ScopedGroupFile test_file(libc_make_test_file_path("reopen_after_end.test"),
+                            CONTENT);
+
+  struct group *grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "root");
+
+  LIBC_NAMESPACE::endgrent();
+
+  // endgrent closes the stream without invalidating the last returned pointer.
+  EXPECT_STREQ(grp->gr_name, "root");
+
+  // Subsequent getgrent after endgrent reopens the database from the start.
+  grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "root");
+
+  LIBC_NAMESPACE::endgrent();
+}
+
+TEST_F(LlvmLibcGrpTest, FileOpenFailure) {
+  auto nonexistent_path = libc_make_test_file_path("getgrent_nonexistent.test");
+  LIBC_NAMESPACE::grp::TESTONLY_set_group_path(nonexistent_path);
+
+  struct group *grp = LIBC_NAMESPACE::getgrent();
+  EXPECT_EQ(grp, nullptr);
+  ASSERT_ERRNO_EQ(ENOENT);
+
+  // POSIX specifies that setgrent() sets errno on error.
+  libc_errno = 0;
+  LIBC_NAMESPACE::setgrent();
+  ASSERT_ERRNO_EQ(ENOENT);
+}
+
+TEST_F(LlvmLibcGrpTest, BlankLines) {
+  constexpr char CONTENT[] = "\n"
+                             "root:x:0:root\n"
+                             "\n"
+                             "\n"
+                             "bin:x:1:bin\n"
+                             "\n";
+  ScopedGroupFile test_file(libc_make_test_file_path("getgrent_blank.test"),
+                            CONTENT);
+
+  struct group *grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "root");
+
+  grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "bin");
+
+  // POSIX mandates that getgrent shall return null and not change errno on EOF.
+  libc_errno = ENOENT;
+  grp = LIBC_NAMESPACE::getgrent();
+  EXPECT_EQ(grp, nullptr);
+  ASSERT_ERRNO_EQ(ENOENT);
+
+  LIBC_NAMESPACE::endgrent();
+}
+
+TEST_F(LlvmLibcGrpTest, DynamicMemberAndLineGrowth) {
+  // Construct a record with 600 members (~3 KB), exercising both dynamic line
+  // growth and per-record sizing of the gr_mem pointer array.
+  constexpr size_t MEMBER_COUNT = 600;
+  constexpr size_t FILE_BUFFER_SIZE = 8192;
+  char content[FILE_BUFFER_SIZE];
+  size_t offset = 0;
+  constexpr char HEADER[] = "biggroup:x:2000:";
+  for (size_t i = 0; HEADER[i] != '\0'; ++i)
+    content[offset++] = HEADER[i];
+
+  for (size_t i = 0; i < MEMBER_COUNT; ++i) {
+    if (i > 0)
+      content[offset++] = ',';
+    content[offset++] = 'u';
+    content[offset++] = '0' + (i / 100) % 10;
+    content[offset++] = '0' + (i / 10) % 10;
+    content[offset++] = '0' + (i % 10);
+  }
+  content[offset++] = '\n';
+  content[offset] = '\0';
+
+  ScopedGroupFile test_file(
+      libc_make_test_file_path("getgrent_dynamic_growth.test"), content);
+
+  struct group *grp = LIBC_NAMESPACE::getgrent();
+  ASSERT_NE(grp, nullptr);
+  EXPECT_STREQ(grp->gr_name, "biggroup");
+  EXPECT_STREQ(grp->gr_passwd, "x");
+  EXPECT_EQ(grp->gr_gid, static_cast<gid_t>(2000));
+  ASSERT_NE(grp->gr_mem, nullptr);
+
+  EXPECT_STREQ(grp->gr_mem[0], "u000");
+  EXPECT_STREQ(grp->gr_mem[MEMBER_COUNT / 2], "u300");
+  EXPECT_STREQ(grp->gr_mem[MEMBER_COUNT - 1], "u599");
+  EXPECT_EQ(grp->gr_mem[MEMBER_COUNT], nullptr);
+
+  LIBC_NAMESPACE::endgrent();
+}
diff --git a/libc/test/src/grp/grp_test_utils.h b/libc/test/src/grp/grp_test_utils.h
new file mode 100644
index 0000000000000..dad4c13449833
--- /dev/null
+++ b/libc/test/src/grp/grp_test_utils.h
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Shared test utilities and fixtures for grp tests.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TEST_SRC_GRP_GRP_TEST_UTILS_H
+#define LLVM_LIBC_TEST_SRC_GRP_GRP_TEST_UTILS_H
+
+#include "hdr/types/size_t.h"
+#include "src/__support/File/file.h"
+#include "src/grp/grp_utils.h"
+#include "src/stdio/remove.h"
+#include "src/string/string_utils.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+// RAII helper class for creating and automatically removing temporary test
+// files, while safely scoping the group database path.
+class ScopedGroupFile {
+  char path[256];
+
+public:
+  ScopedGroupFile(const ScopedGroupFile &) = delete;
+  ScopedGroupFile &operator=(const ScopedGroupFile &) = delete;
+
+  ScopedGroupFile(const char *file_path, const char *content) {
+    LIBC_NAMESPACE::internal::strlcpy(path, file_path, sizeof(path));
+
+    auto file_or = LIBC_NAMESPACE::openfile(path, "w");
+    if (file_or.has_value()) {
+      auto *f = file_or.value();
+      size_t len = LIBC_NAMESPACE::internal::string_length(content);
+      f->write(content, len);
+      f->close();
+    }
+    LIBC_NAMESPACE::grp::TESTONLY_set_group_path(path);
+  }
+
+  ~Scop...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/224372


More information about the libc-commits mailing list