[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