[libc-commits] [libc] [libc] Add getpwent, setpwent, and endpwent functions (PR #206064)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Fri Jul 3 08:17:47 PDT 2026
https://github.com/kaladron updated https://github.com/llvm/llvm-project/pull/206064
>From 48364972057367a05536a73a9bae6e900e67ca8e Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 26 Jun 2026 12:42:06 +0100
Subject: [PATCH 1/2] [libc] Add getpwent, setpwent, and endpwent functions
Implemented `getpwent`, `setpwent`, and `endpwent` functions for
accessing the password database.
* Added `struct passwd` definition in `llvm-libc-types`.
* Added `pwd.yaml` interface definition with POSIX standards.
* Implemented `getpwent`, `setpwent`, and `endpwent` entrypoints.
* Implemented `getpwent` to robustly skip malformed lines by looping.
* Implemented passwd line parsing in an internal helper
`parse_passwd_line` in `pwd_utils`.
* Used `strtointeger` for robust parsing of UID and GID fields.
* Added `struct_passwd` proxy header to avoid direct system includes.
* Centrally declared internal implementation helpers in `pwd_utils.h`.
* Added unit tests for `getpwent` and `setpwent` / `endpwent` behaviour.
Assisted-by: Automated tooling, human reviewed.
---
libc/config/linux/aarch64/entrypoints.txt | 5 ++
libc/config/linux/arm/entrypoints.txt | 5 ++
libc/config/linux/riscv/entrypoints.txt | 5 ++
libc/config/linux/x86_64/entrypoints.txt | 5 ++
libc/hdr/types/CMakeLists.txt | 8 +++
libc/hdr/types/struct_passwd.h | 27 +++++++
libc/include/CMakeLists.txt | 12 ++++
libc/include/llvm-libc-types/CMakeLists.txt | 5 ++
libc/include/llvm-libc-types/struct_passwd.h | 30 ++++++++
libc/include/pwd.yaml | 27 +++++++
libc/src/CMakeLists.txt | 1 +
libc/src/pwd/CMakeLists.txt | 62 ++++++++++++++++
libc/src/pwd/endpwent.cpp | 23 ++++++
libc/src/pwd/endpwent.h | 25 +++++++
libc/src/pwd/getpwent.cpp | 65 +++++++++++++++++
libc/src/pwd/getpwent.h | 26 +++++++
libc/src/pwd/pwd_utils.cpp | 75 ++++++++++++++++++++
libc/src/pwd/pwd_utils.h | 32 +++++++++
libc/src/pwd/setpwent.cpp | 23 ++++++
libc/src/pwd/setpwent.h | 25 +++++++
libc/test/src/CMakeLists.txt | 1 +
libc/test/src/pwd/CMakeLists.txt | 22 ++++++
libc/test/src/pwd/getpwent_test.cpp | 66 +++++++++++++++++
23 files changed, 575 insertions(+)
create mode 100644 libc/hdr/types/struct_passwd.h
create mode 100644 libc/include/llvm-libc-types/struct_passwd.h
create mode 100644 libc/include/pwd.yaml
create mode 100644 libc/src/pwd/CMakeLists.txt
create mode 100644 libc/src/pwd/endpwent.cpp
create mode 100644 libc/src/pwd/endpwent.h
create mode 100644 libc/src/pwd/getpwent.cpp
create mode 100644 libc/src/pwd/getpwent.h
create mode 100644 libc/src/pwd/pwd_utils.cpp
create mode 100644 libc/src/pwd/pwd_utils.h
create mode 100644 libc/src/pwd/setpwent.cpp
create mode 100644 libc/src/pwd/setpwent.h
create mode 100644 libc/test/src/pwd/CMakeLists.txt
create mode 100644 libc/test/src/pwd/getpwent_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 570f7666ba67b..fa55eba8f0321 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -35,6 +35,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 e6aa6091ed550..f55126f9c321c 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -23,6 +23,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/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 2508de5cfe5a4..7d3cfd77dd688 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -35,6 +35,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/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 5ace968a739c8..251957896ff2e 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -48,6 +48,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/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index a130f7ee0000a..93eb08e1f3620 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -1127,3 +1127,11 @@ add_proxy_header_library(
FULL_BUILD_DEPENDS
libc.include.llvm-libc-types.regmatch_t
)
+
+add_proxy_header_library(
+ struct_passwd
+ HDRS
+ struct_passwd.h
+ FULL_BUILD_DEPENDS
+ libc.include.llvm-libc-types.struct_passwd
+)
diff --git a/libc/hdr/types/struct_passwd.h b/libc/hdr/types/struct_passwd.h
new file mode 100644
index 0000000000000..e55667e7172d2
--- /dev/null
+++ b/libc/hdr/types/struct_passwd.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
+/// Proxy for struct passwd.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_STRUCT_PASSWD_H
+#define LLVM_LIBC_HDR_TYPES_STRUCT_PASSWD_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/struct_passwd.h"
+
+#else
+
+#include <pwd.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_STRUCT_PASSWD_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index bb670b614742a..c740dae56f620 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -1034,6 +1034,18 @@ add_header_macro(
.llvm-libc-types.nl_catd
)
+add_header_macro(
+ pwd
+ ../libc/include/pwd.yaml
+ pwd.h
+ DEPENDS
+ .llvm_libc_common_h
+ .llvm-libc-types.struct_passwd
+ .llvm-libc-types.uid_t
+ .llvm-libc-types.gid_t
+ .llvm-libc-types.size_t
+)
+
# UEFI spec references "Uefi.h" so we use that name for compatibility
add_header_macro(
uefi
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 0512d3f0e642a..eeff097d6ceec 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -178,6 +178,11 @@ add_header(
.dev_t .ino_t .mode_t .nlink_t .uid_t .gid_t .off_t .struct_timespec
.blksize_t .blkcnt_t
)
+add_header(
+ struct_passwd
+ HDR struct_passwd.h
+ DEPENDS .gid_t .uid_t
+)
add_header(struct_tm HDR struct_tm.h)
add_header(struct_utsname HDR struct_utsname.h)
add_header(thrd_start_t HDR thrd_start_t.h)
diff --git a/libc/include/llvm-libc-types/struct_passwd.h b/libc/include/llvm-libc-types/struct_passwd.h
new file mode 100644
index 0000000000000..7b41be6edbbe0
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_passwd.h
@@ -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
+/// Definition of struct passwd.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
+#define __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
+
+#include "gid_t.h"
+#include "uid_t.h"
+
+struct passwd {
+ char *pw_name;
+ char *pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+};
+
+#endif // __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
diff --git a/libc/include/pwd.yaml b/libc/include/pwd.yaml
new file mode 100644
index 0000000000000..f8a9500937ed1
--- /dev/null
+++ b/libc/include/pwd.yaml
@@ -0,0 +1,27 @@
+header: pwd.h
+standards:
+ - posix
+macros: []
+types:
+ - type_name: struct_passwd
+ - type_name: uid_t
+ - type_name: gid_t
+ - type_name: size_t
+enums: []
+objects: []
+functions:
+ - name: getpwent
+ standards:
+ - posix
+ return_type: struct passwd *
+ arguments: []
+ - name: setpwent
+ standards:
+ - posix
+ return_type: void
+ arguments: []
+ - name: endpwent
+ standards:
+ - posix
+ return_type: void
+ arguments: []
diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 62aa164b35ee7..a269ac4a83ffd 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -28,6 +28,7 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
add_subdirectory(dirent)
add_subdirectory(fcntl)
add_subdirectory(poll)
+ add_subdirectory(pwd)
add_subdirectory(pthread)
add_subdirectory(sched)
add_subdirectory(semaphore)
diff --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt
new file mode 100644
index 0000000000000..b808f59492e11
--- /dev/null
+++ b/libc/src/pwd/CMakeLists.txt
@@ -0,0 +1,62 @@
+#===-- CMakeLists.txt ----------------------------------------------------===#
+#
+# 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
+#
+#===----------------------------------------------------------------------===#
+
+add_object_library(
+ pwd_utils
+ SRCS
+ pwd_utils.cpp
+ HDRS
+ pwd_utils.h
+ DEPENDS
+ libc.hdr.types.struct_passwd
+ libc.src.__support.str_to_integer
+ libc.src.string.string_utils
+)
+
+add_entrypoint_object(
+ getpwent
+ SRCS
+ getpwent.cpp
+ HDRS
+ getpwent.h
+ DEPENDS
+ libc.include.pwd
+ libc.hdr.types.struct_passwd
+ libc.src.errno.errno
+ libc.src.stdio.fopen
+ libc.src.stdio.fclose
+ libc.src.stdio.fgets
+ libc.src.stdio.fseek
+ libc.src.string.string_utils
+ libc.hdr.stdio_macros
+ .pwd_utils
+)
+
+add_entrypoint_object(
+ setpwent
+ SRCS
+ setpwent.cpp
+ HDRS
+ setpwent.h
+ DEPENDS
+ libc.include.pwd
+ .getpwent
+ .pwd_utils
+)
+
+add_entrypoint_object(
+ endpwent
+ SRCS
+ endpwent.cpp
+ HDRS
+ endpwent.h
+ DEPENDS
+ libc.include.pwd
+ .getpwent
+ .pwd_utils
+)
diff --git a/libc/src/pwd/endpwent.cpp b/libc/src/pwd/endpwent.cpp
new file mode 100644
index 0000000000000..948eac2b72349
--- /dev/null
+++ b/libc/src/pwd/endpwent.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/macros/config.h"
+#include "src/pwd/pwd_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, endpwent, ()) { endpwent_impl(); }
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/endpwent.h b/libc/src/pwd/endpwent.h
new file mode 100644
index 0000000000000..b5a8262cc7d33
--- /dev/null
+++ b/libc/src/pwd/endpwent.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Declarations of endpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_ENDPWENT_H
+#define LLVM_LIBC_SRC_PWD_ENDPWENT_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+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..83bbe1ade4137
--- /dev/null
+++ b/libc/src/pwd/getpwent.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/__support/macros/config.h"
+#include "src/pwd/pwd_utils.h"
+#include "src/stdio/fclose.h"
+#include "src/stdio/fgets.h"
+#include "src/stdio/fopen.h"
+#include "src/string/string_utils.h"
+
+#include "hdr/stdio_macros.h"
+#include "src/stdio/fseek.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static FILE *pwd_file = nullptr;
+static char line_buffer[1024];
+static struct passwd pwd_entry;
+
+void setpwent_impl() {
+ if (pwd_file)
+ LIBC_NAMESPACE::fseek(pwd_file, 0, SEEK_SET);
+}
+
+void endpwent_impl() {
+ if (pwd_file) {
+ LIBC_NAMESPACE::fclose(pwd_file);
+ pwd_file = nullptr;
+ }
+}
+
+LLVM_LIBC_FUNCTION(struct passwd *, getpwent, ()) {
+ if (!pwd_file) {
+ pwd_file = LIBC_NAMESPACE::fopen("/etc/passwd", "r");
+ if (!pwd_file)
+ return nullptr;
+ }
+
+ while (LIBC_NAMESPACE::fgets(line_buffer, sizeof(line_buffer), pwd_file)) {
+ // Remove newline
+ size_t len = LIBC_NAMESPACE::internal::string_length(line_buffer);
+ if (len > 0 && line_buffer[len - 1] == '\n')
+ line_buffer[len - 1] = '\0';
+
+ if (internal::parse_passwd_line(line_buffer, &pwd_entry))
+ return &pwd_entry;
+ }
+
+ return nullptr;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/getpwent.h b/libc/src/pwd/getpwent.h
new file mode 100644
index 0000000000000..5e8e96b8f6ba1
--- /dev/null
+++ b/libc/src/pwd/getpwent.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
+/// Declarations of getpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#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 {
+
+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
new file mode 100644
index 0000000000000..6a993c1129c5a
--- /dev/null
+++ b/libc/src/pwd/pwd_utils.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Helper functions for pwd.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/pwd_utils.h"
+#include "src/__support/str_to_integer.h"
+#include "src/string/string_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+bool parse_passwd_line(char *line, struct passwd *pwd) {
+ if (!line || !pwd)
+ return false;
+
+ char *context = line;
+
+ pwd->pw_name =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_name)
+ return false;
+
+ pwd->pw_passwd =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_passwd)
+ return false;
+
+ char *uid_str =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!uid_str)
+ return false;
+ auto uid_res = LIBC_NAMESPACE::internal::strtointeger<uid_t>(uid_str, 10);
+ if (uid_res.has_error())
+ return false;
+ pwd->pw_uid = uid_res;
+
+ char *gid_str =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!gid_str)
+ return false;
+ auto gid_res = LIBC_NAMESPACE::internal::strtointeger<gid_t>(gid_str, 10);
+ if (gid_res.has_error())
+ return false;
+ pwd->pw_gid = gid_res;
+
+ pwd->pw_gecos =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_gecos)
+ return false;
+
+ pwd->pw_dir =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_dir)
+ return false;
+
+ // shell
+ pwd->pw_shell =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_shell)
+ return false;
+
+ return true;
+}
+
+} // namespace internal
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/pwd_utils.h b/libc/src/pwd/pwd_utils.h
new file mode 100644
index 0000000000000..2ff5b3211dbb6
--- /dev/null
+++ b/libc/src/pwd/pwd_utils.h
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Declarations of helper functions for pwd.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_PWD_UTILS_H
+#define LLVM_LIBC_SRC_PWD_PWD_UTILS_H
+
+#include "hdr/types/struct_passwd.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+bool parse_passwd_line(char *line, struct passwd *pwd);
+
+} // namespace internal
+
+void setpwent_impl();
+void endpwent_impl();
+
+} // 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..a4efcdacba2dc
--- /dev/null
+++ b/libc/src/pwd/setpwent.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/macros/config.h"
+#include "src/pwd/pwd_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, setpwent, ()) { setpwent_impl(); }
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/setpwent.h b/libc/src/pwd/setpwent.h
new file mode 100644
index 0000000000000..91cb263b41d90
--- /dev/null
+++ b/libc/src/pwd/setpwent.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Declarations of setpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_SETPWENT_H
+#define LLVM_LIBC_SRC_PWD_SETPWENT_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+void setpwent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_SETPWENT_H
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index 45815c9bba8ca..c0617d83c30ab 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -87,6 +87,7 @@ add_subdirectory(inttypes)
if(${LIBC_TARGET_OS} STREQUAL "linux")
add_subdirectory(fcntl)
add_subdirectory(poll)
+ add_subdirectory(pwd)
add_subdirectory(sched)
add_subdirectory(sys)
add_subdirectory(termios)
diff --git a/libc/test/src/pwd/CMakeLists.txt b/libc/test/src/pwd/CMakeLists.txt
new file mode 100644
index 0000000000000..b5bf000443b38
--- /dev/null
+++ b/libc/test/src/pwd/CMakeLists.txt
@@ -0,0 +1,22 @@
+#===-- CMakeLists.txt ----------------------------------------------------===#
+#
+# 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
+#
+#===----------------------------------------------------------------------===#
+
+add_custom_target(libc_pwd_unittests)
+
+add_libc_unittest(
+ getpwent_test
+ SUITE
+ libc_pwd_unittests
+ SRCS
+ getpwent_test.cpp
+ DEPENDS
+ libc.src.pwd.getpwent
+ libc.src.pwd.setpwent
+ libc.src.pwd.endpwent
+ libc.src.string.strcmp
+)
diff --git a/libc/test/src/pwd/getpwent_test.cpp b/libc/test/src/pwd/getpwent_test.cpp
new file mode 100644
index 0000000000000..eabc09e502f69
--- /dev/null
+++ b/libc/test/src/pwd/getpwent_test.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Unittests for getpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/endpwent.h"
+#include "src/pwd/getpwent.h"
+#include "src/pwd/setpwent.h"
+#include "src/string/strcmp.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcPwdTest, GetPwentTest) {
+ // We assume /etc/passwd exists and contains at least "root" and "daemon".
+ // We also assume it does NOT contain "baduser".
+
+ bool found_root = false;
+ bool found_daemon = false;
+ bool found_baduser = false;
+
+ LIBC_NAMESPACE::setpwent();
+
+ struct passwd *pw;
+ while ((pw = LIBC_NAMESPACE::getpwent()) != nullptr) {
+ if (LIBC_NAMESPACE::strcmp(pw->pw_name, "root") == 0) {
+ found_root = true;
+ ASSERT_EQ(pw->pw_uid, static_cast<uid_t>(0));
+ }
+ if (LIBC_NAMESPACE::strcmp(pw->pw_name, "daemon") == 0) {
+ found_daemon = true;
+ }
+ if (LIBC_NAMESPACE::strcmp(pw->pw_name, "baduser") == 0) {
+ found_baduser = true;
+ }
+ }
+
+ ASSERT_TRUE(found_root);
+ ASSERT_TRUE(found_daemon);
+ ASSERT_FALSE(found_baduser);
+
+ LIBC_NAMESPACE::endpwent();
+}
+
+TEST(LlvmLibcPwdTest, SetPwentTest) {
+ // Read first entry
+ LIBC_NAMESPACE::setpwent();
+ struct passwd *pw1 = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw1 != nullptr);
+
+ // Rewind
+ LIBC_NAMESPACE::setpwent();
+ struct passwd *pw2 = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw2 != nullptr);
+
+ // Should be the same entry
+ ASSERT_STREQ(pw1->pw_name, pw2->pw_name);
+
+ LIBC_NAMESPACE::endpwent();
+}
>From 20b48bd4e24ccc35dd731eea7ef025f363e71e70 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 3 Jul 2026 16:17:34 +0100
Subject: [PATCH 2/2] [libc] Use internal File class in getpwent
Rewrite getpwent to use the internal File class instead of public
entrypoints (fopen, fgets, fclose, fseek). This resolves link errors
in overlay mode where these stdio entrypoints are not built.
Also update struct_passwd.h type header guard to remove double
underscores.
Assisted-by: Automated tooling, human reviewed.
---
libc/include/llvm-libc-types/struct_passwd.h | 6 +--
libc/src/pwd/CMakeLists.txt | 6 +--
libc/src/pwd/getpwent.cpp | 49 ++++++++++++++++----
3 files changed, 44 insertions(+), 17 deletions(-)
diff --git a/libc/include/llvm-libc-types/struct_passwd.h b/libc/include/llvm-libc-types/struct_passwd.h
index 7b41be6edbbe0..0f0baf743627b 100644
--- a/libc/include/llvm-libc-types/struct_passwd.h
+++ b/libc/include/llvm-libc-types/struct_passwd.h
@@ -11,8 +11,8 @@
///
//===----------------------------------------------------------------------===//
-#ifndef __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
-#define __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
+#ifndef LLVM_LIBC_TYPES_STRUCT_PASSWD_H
+#define LLVM_LIBC_TYPES_STRUCT_PASSWD_H
#include "gid_t.h"
#include "uid_t.h"
@@ -27,4 +27,4 @@ struct passwd {
char *pw_shell;
};
-#endif // __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
+#endif // LLVM_LIBC_TYPES_STRUCT_PASSWD_H
diff --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt
index b808f59492e11..0d1706bd81d03 100644
--- a/libc/src/pwd/CMakeLists.txt
+++ b/libc/src/pwd/CMakeLists.txt
@@ -28,10 +28,8 @@ add_entrypoint_object(
libc.include.pwd
libc.hdr.types.struct_passwd
libc.src.errno.errno
- libc.src.stdio.fopen
- libc.src.stdio.fclose
- libc.src.stdio.fgets
- libc.src.stdio.fseek
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
libc.src.string.string_utils
libc.hdr.stdio_macros
.pwd_utils
diff --git a/libc/src/pwd/getpwent.cpp b/libc/src/pwd/getpwent.cpp
index 83bbe1ade4137..7e6982101a6c2 100644
--- a/libc/src/pwd/getpwent.cpp
+++ b/libc/src/pwd/getpwent.cpp
@@ -12,44 +12,73 @@
//===----------------------------------------------------------------------===//
#include "src/pwd/getpwent.h"
+#include "src/__support/File/file.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/pwd/pwd_utils.h"
-#include "src/stdio/fclose.h"
-#include "src/stdio/fgets.h"
-#include "src/stdio/fopen.h"
#include "src/string/string_utils.h"
#include "hdr/stdio_macros.h"
-#include "src/stdio/fseek.h"
namespace LIBC_NAMESPACE_DECL {
-static FILE *pwd_file = nullptr;
+static File *pwd_file = nullptr;
static char line_buffer[1024];
static struct passwd pwd_entry;
void setpwent_impl() {
if (pwd_file)
- LIBC_NAMESPACE::fseek(pwd_file, 0, SEEK_SET);
+ pwd_file->seek(0, SEEK_SET);
}
void endpwent_impl() {
if (pwd_file) {
- LIBC_NAMESPACE::fclose(pwd_file);
+ pwd_file->close();
pwd_file = nullptr;
}
}
+static bool read_line(File *f, char *buf, size_t max_len) {
+ if (max_len < 1)
+ return false;
+
+ unsigned char c = '\0';
+ f->lock();
+ size_t i = 0;
+ FileIOResult result(0);
+ for (; i < (max_len - 1) && c != '\n'; ++i) {
+ result = f->read_unlocked(&c, 1);
+ if (result.has_error()) {
+ libc_errno = result.error;
+ break;
+ }
+ if (result.value != 1)
+ break;
+ buf[i] = c;
+ }
+ bool has_error = f->error_unlocked();
+ bool has_eof = f->iseof_unlocked();
+ f->unlock();
+
+ if (has_error || (i == 0 && has_eof))
+ return false;
+
+ buf[i] = '\0';
+ return true;
+}
+
LLVM_LIBC_FUNCTION(struct passwd *, getpwent, ()) {
if (!pwd_file) {
- pwd_file = LIBC_NAMESPACE::fopen("/etc/passwd", "r");
- if (!pwd_file)
+ auto result = LIBC_NAMESPACE::openfile("/etc/passwd", "r");
+ if (!result.has_value()) {
+ libc_errno = result.error();
return nullptr;
+ }
+ pwd_file = result.value();
}
- while (LIBC_NAMESPACE::fgets(line_buffer, sizeof(line_buffer), pwd_file)) {
+ while (read_line(pwd_file, line_buffer, sizeof(line_buffer))) {
// Remove newline
size_t len = LIBC_NAMESPACE::internal::string_length(line_buffer);
if (len > 0 && line_buffer[len - 1] == '\n')
More information about the libc-commits
mailing list