[libc-commits] [libc] 096481d - [libc] Add getpwent, setpwent, and endpwent entrypoints (#213076)

via libc-commits libc-commits at lists.llvm.org
Fri Aug 7 02:09:21 PDT 2026


Author: Jeff Bailey
Date: 2026-08-07T10:09:15+01:00
New Revision: 096481ddc5aa696f26ae4fd704adb28ce31babc6

URL: https://github.com/llvm/llvm-project/commit/096481ddc5aa696f26ae4fd704adb28ce31babc6
DIFF: https://github.com/llvm/llvm-project/commit/096481ddc5aa696f26ae4fd704adb28ce31babc6.diff

LOG: [libc] Add getpwent, setpwent, and endpwent entrypoints (#213076)

Added getpwent, setpwent, and endpwent functions using the internal
pwd_utils line parser.

* Implemented getpwent.cpp, setpwent.cpp, and endpwent.cpp entrypoints
* Added database iteration state and helpers (open, close, read_next)
under namespace passwd in pwd_utils
* Registered entrypoints in config/linux/*/entrypoints.txt
* Added CMake target guards for File and platform_file to support
overlay builds and cross-platform targets
* Added unit tests in libc/test/src/pwd/getpwent_test.cpp

Assisted-by: Automated tooling, human reviewed.

Added: 
    libc/src/pwd/endpwent.cpp
    libc/src/pwd/endpwent.h
    libc/src/pwd/getpwent.cpp
    libc/src/pwd/getpwent.h
    libc/src/pwd/setpwent.cpp
    libc/src/pwd/setpwent.h
    libc/test/src/pwd/getpwent_test.cpp

Modified: 
    libc/config/linux/aarch64/entrypoints.txt
    libc/config/linux/arm/entrypoints.txt
    libc/config/linux/i386/entrypoints.txt
    libc/config/linux/riscv/entrypoints.txt
    libc/config/linux/x86_64/entrypoints.txt
    libc/src/CMakeLists.txt
    libc/src/pwd/CMakeLists.txt
    libc/src/pwd/pwd_utils.cpp
    libc/src/pwd/pwd_utils.h
    libc/test/src/pwd/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 1d5244b9c443e..5686c6e095254 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -45,6 +45,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     # poll.h entrypoints
     libc.src.poll.poll
 
+    # pwd.h entrypoints
+    libc.src.pwd.endpwent
+    libc.src.pwd.getpwent
+    libc.src.pwd.setpwent
+
     # sched.h entrypoints
     libc.src.sched.sched_get_priority_max
     libc.src.sched.sched_get_priority_min

diff  --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 1986d6a5347dc..f80a0acaf7c10 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -33,6 +33,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     # poll.h entrypoints
     libc.src.poll.poll
 
+    # pwd.h entrypoints
+    libc.src.pwd.endpwent
+    libc.src.pwd.getpwent
+    libc.src.pwd.setpwent
+
     # string.h entrypoints
     libc.src.string.memccpy
     libc.src.string.memchr

diff  --git a/libc/config/linux/i386/entrypoints.txt b/libc/config/linux/i386/entrypoints.txt
index 6548c9b816c93..3942289a2e5b2 100644
--- a/libc/config/linux/i386/entrypoints.txt
+++ b/libc/config/linux/i386/entrypoints.txt
@@ -1,6 +1,11 @@
 set(TARGET_LIBC_ENTRYPOINTS
   # errno.h entrypoints
   libc.src.errno.errno
+
+  # pwd.h entrypoints
+  libc.src.pwd.endpwent
+  libc.src.pwd.getpwent
+  libc.src.pwd.setpwent
 )
 
 set(TARGET_LIBM_ENTRYPOINTS "")

diff  --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 0f7822b36a761..f95cfd3820708 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -64,6 +64,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     # poll.h entrypoints
     libc.src.poll.poll
 
+    # pwd.h entrypoints
+    libc.src.pwd.endpwent
+    libc.src.pwd.getpwent
+    libc.src.pwd.setpwent
+
     # sched.h entrypoints
     libc.src.sched.getcpu
     libc.src.sched.sched_get_priority_max

diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 7a9e741310c6a..e257bc19b2920 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -64,6 +64,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     # poll.h entrypoints
     libc.src.poll.poll
 
+    # pwd.h entrypoints
+    libc.src.pwd.endpwent
+    libc.src.pwd.getpwent
+    libc.src.pwd.setpwent
+
     # sched.h entrypoints
     libc.src.sched.getcpu
     libc.src.sched.sched_get_priority_max

diff  --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 05944b582b5f8..4e7aaa1112ed7 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -31,6 +31,7 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
   add_subdirectory(fcntl)
   add_subdirectory(poll)
   add_subdirectory(pthread)
+  add_subdirectory(pwd)
   add_subdirectory(sched)
   add_subdirectory(semaphore)
   add_subdirectory(sys)
@@ -56,4 +57,3 @@ add_subdirectory(signal)
 add_subdirectory(spawn)
 add_subdirectory(threads)
 add_subdirectory(ucontext)
-add_subdirectory(pwd)

diff  --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt
index f8498b27eaa54..4cde157adf673 100644
--- a/libc/src/pwd/CMakeLists.txt
+++ b/libc/src/pwd/CMakeLists.txt
@@ -1,3 +1,47 @@
+if(NOT TARGET libc.src.__support.File.file OR NOT TARGET libc.src.__support.File.platform_file)
+  return()
+endif()
+
+add_entrypoint_object(
+  getpwent
+  SRCS
+    getpwent.cpp
+  HDRS
+    getpwent.h
+  DEPENDS
+    libc.hdr.types.struct_passwd
+    libc.src.errno.errno
+    libc.src.__support.common
+    libc.src.__support.macros.config
+    .pwd_utils
+)
+
+add_entrypoint_object(
+  setpwent
+  SRCS
+    setpwent.cpp
+  HDRS
+    setpwent.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.__support.common
+    libc.src.__support.macros.config
+    .pwd_utils
+)
+
+add_entrypoint_object(
+  endpwent
+  SRCS
+    endpwent.cpp
+  HDRS
+    endpwent.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.__support.common
+    libc.src.__support.macros.config
+    .pwd_utils
+)
+
 add_object_library(
   pwd_utils
   HDRS
@@ -6,8 +50,12 @@ add_object_library(
     pwd_utils.cpp
   DEPENDS
     libc.hdr.errno_macros
+    libc.hdr.stdio_macros
     libc.hdr.types.struct_passwd
     libc.src.string.string_utils
+    libc.src.__support.CPP.span
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
     libc.src.__support.ctype_utils
     libc.src.__support.error_or
     libc.src.__support.str_to_integer

diff  --git a/libc/src/pwd/endpwent.cpp b/libc/src/pwd/endpwent.cpp
new file mode 100644
index 0000000000000..e5edde36d0d2d
--- /dev/null
+++ b/libc/src/pwd/endpwent.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 endpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/endpwent.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/pwd/pwd_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, endpwent, ()) {
+  auto res = passwd::close();
+  if (!res.has_value())
+    libc_errno = res.error();
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/pwd/endpwent.h b/libc/src/pwd/endpwent.h
new file mode 100644
index 0000000000000..e23ef3ea6dadf
--- /dev/null
+++ b/libc/src/pwd/endpwent.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 endpwent function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_ENDPWENT_H
+#define LLVM_LIBC_SRC_PWD_ENDPWENT_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Closes the password database stream and releases associated resources.
+void endpwent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_ENDPWENT_H

diff  --git a/libc/src/pwd/getpwent.cpp b/libc/src/pwd/getpwent.cpp
new file mode 100644
index 0000000000000..ab0f8d05f2794
--- /dev/null
+++ b/libc/src/pwd/getpwent.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 getpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/getpwent.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/pwd/pwd_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(struct passwd *, getpwent, ()) {
+  auto res = passwd::read_next();
+  if (!res.has_value()) {
+    libc_errno = res.error();
+    return nullptr;
+  }
+  return res.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/pwd/getpwent.h b/libc/src/pwd/getpwent.h
new file mode 100644
index 0000000000000..dcafd7bdba32e
--- /dev/null
+++ b/libc/src/pwd/getpwent.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 getpwent function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_GETPWENT_H
+#define LLVM_LIBC_SRC_PWD_GETPWENT_H
+
+#include "hdr/types/struct_passwd.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Reads the next entry from the password database.
+struct passwd *getpwent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_GETPWENT_H

diff  --git a/libc/src/pwd/pwd_utils.cpp b/libc/src/pwd/pwd_utils.cpp
index f56e6118328e8..aaf0faf37e4ea 100644
--- a/libc/src/pwd/pwd_utils.cpp
+++ b/libc/src/pwd/pwd_utils.cpp
@@ -13,11 +13,18 @@
 
 #include "src/pwd/pwd_utils.h"
 #include "hdr/errno_macros.h"
+#include "hdr/stdio_macros.h"
 #include "hdr/types/struct_passwd.h"
+#include "src/__support/CPP/span.h"
+#include "src/__support/File/file.h"
 #include "src/__support/ctype_utils.h"
 #include "src/__support/str_to_integer.h"
 #include "src/string/string_utils.h"
 
+#ifndef LIBC_COPT_PWD_FILE_PATH
+#define LIBC_COPT_PWD_FILE_PATH "/etc/passwd"
+#endif
+
 namespace LIBC_NAMESPACE_DECL {
 namespace internal {
 
@@ -70,4 +77,133 @@ ErrorOr<struct passwd> parse_passwd_line(char *line) {
 }
 
 } // namespace internal
+
+namespace passwd {
+
+static File *pwd_file = nullptr;
+static const char *pwd_file_path = LIBC_COPT_PWD_FILE_PATH;
+// Note: These static buffers are process-global and NOT protected by a mutex
+// at this stage. POSIX getpwent is non-reentrant.
+static char line_buffer[1024];
+static struct passwd pwd_entry;
+
+void TESTONLY_set_passwd_path(const char *path) {
+  if (!path)
+    return;
+  if (pwd_file) {
+    pwd_file->close();
+    pwd_file = nullptr;
+  }
+  pwd_file_path = path;
+}
+
+ErrorOr<int> open() {
+  if (!pwd_file) {
+    auto result = openfile(pwd_file_path, "r");
+    if (!result.has_value())
+      return Error(result.error());
+    pwd_file = result.value();
+  } else {
+    auto result = pwd_file->seek(0, SEEK_SET);
+    if (!result.has_value())
+      return Error(result.error());
+  }
+  return 0;
+}
+
+ErrorOr<int> close() {
+  if (pwd_file) {
+    int result = pwd_file->close();
+    pwd_file = nullptr;
+    if (result != 0)
+      return Error(result);
+  }
+  return 0;
+}
+
+struct ReadLineResult {
+  size_t bytes_read;
+  bool truncated;
+};
+
+// Reads a line from the given file into buf.
+static ErrorOr<ReadLineResult> read_line(File *f, cpp::span<char> buf) {
+  if (!f || buf.empty())
+    return Error(EINVAL);
+
+  f->lock();
+  size_t bytes_read = 0;
+  FileIOResult result(0);
+  bool truncated = false;
+
+  for (char &ch : buf.first(buf.size() - 1)) {
+    result = f->read_unlocked(&ch, 1);
+    if (result.has_error()) {
+      f->unlock();
+      return Error(result.error);
+    }
+    if (result.value != 1)
+      break;
+    ++bytes_read;
+    if (ch == '\n')
+      break;
+  }
+
+  if (result.value == 1 && bytes_read > 0 && buf[bytes_read - 1] != '\n') {
+    truncated = true;
+    char c = '\0';
+    while (true) {
+      result = f->read_unlocked(&c, 1);
+      if (result.has_error()) {
+        f->unlock();
+        return Error(result.error);
+      }
+      if (result.value != 1 || c == '\n')
+        break;
+    }
+  }
+
+  bool has_error = f->error_unlocked();
+  f->unlock();
+
+  if (has_error)
+    return Error(EIO);
+
+  buf[bytes_read] = '\0';
+  return ReadLineResult{bytes_read, truncated};
+}
+
+ErrorOr<struct passwd *> read_next() {
+  if (!pwd_file) {
+    auto result = open();
+    if (!result.has_value())
+      return Error(result.error());
+  }
+
+  while (true) {
+    auto result = read_line(pwd_file, line_buffer);
+    if (!result.has_value())
+      return Error(result.error());
+
+    ReadLineResult res = result.value();
+    if (res.bytes_read == 0)
+      return nullptr;
+
+    if (res.truncated)
+      return Error(EINVAL);
+
+    size_t len = res.bytes_read;
+    if (len > 0 && line_buffer[len - 1] == '\n')
+      line_buffer[len - 1] = '\0';
+
+    auto passwd_or = internal::parse_passwd_line(line_buffer);
+    if (!passwd_or.has_value())
+      return Error(passwd_or.error());
+
+    pwd_entry = passwd_or.value();
+    return &pwd_entry;
+  }
+}
+
+} // namespace passwd
 } // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/pwd/pwd_utils.h b/libc/src/pwd/pwd_utils.h
index fe3b48179c54e..7b48b2cabc8f5 100644
--- a/libc/src/pwd/pwd_utils.h
+++ b/libc/src/pwd/pwd_utils.h
@@ -25,6 +25,22 @@ namespace internal {
 ErrorOr<struct passwd> parse_passwd_line(char *line);
 
 } // namespace internal
+
+namespace passwd {
+
+// Overrides the default password file path for testing purposes.
+void TESTONLY_set_passwd_path(const char *path);
+
+// Opens or rewinds the password file.
+ErrorOr<int> open();
+
+// Closes the password file.
+ErrorOr<int> close();
+
+// Reads the next entry from the password database.
+ErrorOr<struct passwd *> read_next();
+
+} // namespace passwd
 } // namespace LIBC_NAMESPACE_DECL
 
 #endif // LLVM_LIBC_SRC_PWD_PWD_UTILS_H

diff  --git a/libc/src/pwd/setpwent.cpp b/libc/src/pwd/setpwent.cpp
new file mode 100644
index 0000000000000..effedd493caf7
--- /dev/null
+++ b/libc/src/pwd/setpwent.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 setpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/setpwent.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/pwd/pwd_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, setpwent, ()) {
+  auto res = passwd::open();
+  if (!res.has_value())
+    libc_errno = res.error();
+}
+
+} // namespace LIBC_NAMESPACE_DECL

diff  --git a/libc/src/pwd/setpwent.h b/libc/src/pwd/setpwent.h
new file mode 100644
index 0000000000000..90dda5f9c7c2b
--- /dev/null
+++ b/libc/src/pwd/setpwent.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 setpwent function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_SETPWENT_H
+#define LLVM_LIBC_SRC_PWD_SETPWENT_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Rewinds the password database stream to the beginning.
+void setpwent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_SETPWENT_H

diff  --git a/libc/test/src/pwd/CMakeLists.txt b/libc/test/src/pwd/CMakeLists.txt
index eb6c96f2f296f..06b7d59febe11 100644
--- a/libc/test/src/pwd/CMakeLists.txt
+++ b/libc/test/src/pwd/CMakeLists.txt
@@ -1,5 +1,9 @@
 add_custom_target(libc_pwd_unittests)
 
+if(NOT TARGET libc.src.pwd.pwd_utils)
+  return()
+endif()
+
 add_libc_unittest(
   pwd_utils_test
   SUITE
@@ -11,3 +15,22 @@ add_libc_unittest(
     libc.hdr.types.struct_passwd
     libc.src.pwd.pwd_utils
 )
+
+add_libc_unittest(
+  getpwent_test
+  SUITE
+    libc_pwd_unittests
+  SRCS
+    getpwent_test.cpp
+  DEPENDS
+    libc.hdr.types.struct_passwd
+    libc.src.__support.File.file
+    libc.src.__support.File.platform_file
+    libc.src.errno.errno
+    libc.src.pwd.endpwent
+    libc.src.pwd.getpwent
+    libc.src.pwd.pwd_utils
+    libc.src.pwd.setpwent
+    libc.src.stdio.remove
+    libc.src.string.string_utils
+)

diff  --git a/libc/test/src/pwd/getpwent_test.cpp b/libc/test/src/pwd/getpwent_test.cpp
new file mode 100644
index 0000000000000..5fca34d9b3b2b
--- /dev/null
+++ b/libc/test/src/pwd/getpwent_test.cpp
@@ -0,0 +1,152 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 getpwent, setpwent, and endpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/types/struct_passwd.h"
+#include "src/__support/File/file.h"
+#include "src/__support/libc_errno.h"
+#include "src/pwd/endpwent.h"
+#include "src/pwd/getpwent.h"
+#include "src/pwd/pwd_utils.h"
+#include "src/pwd/setpwent.h"
+#include "src/stdio/remove.h"
+#include "src/string/string_utils.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+
+namespace {
+
+// RAII helper class for creating and automatically removing temporary test
+// files.
+class HermeticFile {
+  char path[256];
+
+public:
+  HermeticFile(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();
+    }
+  }
+
+  ~HermeticFile() { LIBC_NAMESPACE::remove(path); }
+
+  const char *get_path() const { return path; }
+};
+
+class LlvmLibcPwdTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {};
+
+} // namespace
+
+TEST_F(LlvmLibcPwdTest, GetPwentTestSuccess) {
+  const char *content = "root:x:0:0:root:/root:/bin/bash\n"
+                        "bin:x:1:1:bin:/bin:/sbin/nologin\n";
+  HermeticFile test_file(libc_make_test_file_path("getpwent_success.test"),
+                         content);
+
+  LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+  LIBC_NAMESPACE::setpwent();
+
+  struct passwd *pwd1 = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd1 != nullptr);
+  ASSERT_STREQ(pwd1->pw_name, "root");
+  ASSERT_EQ(pwd1->pw_uid, 0u);
+
+  struct passwd *pwd2 = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd2 != nullptr);
+  ASSERT_STREQ(pwd2->pw_name, "bin");
+  ASSERT_EQ(pwd2->pw_uid, 1u);
+
+  struct passwd *pwd3 = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd3 == nullptr);
+
+  LIBC_NAMESPACE::endpwent();
+}
+
+TEST_F(LlvmLibcPwdTest, GetPwentTestFailure) {
+  const char *content = "invalid_line_without_enough_fields\n";
+  HermeticFile test_file(libc_make_test_file_path("getpwent_fail.test"),
+                         content);
+
+  LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+  LIBC_NAMESPACE::setpwent();
+
+  struct passwd *pwd = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd == nullptr);
+  ASSERT_ERRNO_EQ(EINVAL);
+
+  LIBC_NAMESPACE::endpwent();
+}
+
+TEST_F(LlvmLibcPwdTest, SetPwentTestHermetic) {
+  const char *content = "user1:x:1000:1000:User One:/home/user1:/bin/bash\n"
+                        "user2:x:1001:1001:User Two:/home/user2:/bin/bash\n";
+  HermeticFile test_file(libc_make_test_file_path("setpwent_hermetic.test"),
+                         content);
+
+  LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+  struct passwd *pwd = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd != nullptr);
+  ASSERT_STREQ(pwd->pw_name, "user1");
+
+  pwd = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd != nullptr);
+  ASSERT_STREQ(pwd->pw_name, "user2");
+
+  // Reset iteration
+  LIBC_NAMESPACE::setpwent();
+
+  pwd = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd != nullptr);
+  ASSERT_STREQ(pwd->pw_name, "user1");
+
+  LIBC_NAMESPACE::endpwent();
+}
+
+TEST_F(LlvmLibcPwdTest, ReopenAfterEndpwent) {
+  const char *content = "root:x:0:0:root:/root:/bin/bash\n";
+  HermeticFile test_file(libc_make_test_file_path("reopen_endpwent.test"),
+                         content);
+
+  LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+  struct passwd *pwd = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd != nullptr);
+  ASSERT_STREQ(pwd->pw_name, "root");
+
+  LIBC_NAMESPACE::endpwent();
+
+  pwd = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd != nullptr);
+  ASSERT_STREQ(pwd->pw_name, "root");
+
+  LIBC_NAMESPACE::endpwent();
+}
+
+TEST_F(LlvmLibcPwdTest, FileOpenFailure) {
+  LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(
+      "/nonexistent_directory/nonexistent_file");
+  LIBC_NAMESPACE::endpwent(); // Force close any existing file
+
+  struct passwd *pwd = LIBC_NAMESPACE::getpwent();
+  ASSERT_TRUE(pwd == nullptr);
+  ASSERT_ERRNO_EQ(ENOENT);
+}


        


More information about the libc-commits mailing list