[libc-commits] [libc] [libc] Refactor statfs Linux syscalls and provide 'struct statfs' (PR #212930)

Alexey Samsonov via libc-commits libc-commits at lists.llvm.org
Thu Jul 30 07:18:39 PDT 2026


https://github.com/vonosmas updated https://github.com/llvm/llvm-project/pull/212930

>From b31f286d37916c0961a9660783caa7a12ea557b2 Mon Sep 17 00:00:00 2001
From: Alexey Samsonov <vonosmas at gmail.com>
Date: Thu, 30 Jul 2026 05:16:23 +0000
Subject: [PATCH 1/4] [libc] Refactor statfs Linux syscalls and provide 'struct
 statfs'

---
 libc/hdr/types/CMakeLists.txt                 |  8 +++
 libc/hdr/types/struct_statfs.h                | 27 ++++++++
 libc/include/llvm-libc-types/CMakeLists.txt   | 10 +++
 libc/include/llvm-libc-types/fsid_t.h         | 21 ++++++
 libc/include/llvm-libc-types/struct_statfs.h  | 36 ++++++++++
 .../linux/syscall_wrappers/CMakeLists.txt     | 26 +++++++
 .../OSUtil/linux/syscall_wrappers/fstatfs.h   | 38 +++++++++++
 .../OSUtil/linux/syscall_wrappers/statfs.h    | 38 +++++++++++
 libc/src/sys/statvfs/linux/CMakeLists.txt     | 10 +--
 libc/src/sys/statvfs/linux/fstatvfs.cpp       | 16 +++--
 libc/src/sys/statvfs/linux/statfs_utils.h     | 67 +++----------------
 libc/src/sys/statvfs/linux/statvfs.cpp        | 16 +++--
 libc/src/unistd/linux/CMakeLists.txt          | 11 ++-
 libc/src/unistd/linux/fpathconf.cpp           | 16 +++--
 libc/src/unistd/linux/pathconf.cpp            | 14 ++--
 libc/src/unistd/linux/pathconf_utils.cpp      | 11 ++-
 libc/src/unistd/linux/pathconf_utils.h        |  4 +-
 17 files changed, 271 insertions(+), 98 deletions(-)
 create mode 100644 libc/hdr/types/struct_statfs.h
 create mode 100644 libc/include/llvm-libc-types/fsid_t.h
 create mode 100644 libc/include/llvm-libc-types/struct_statfs.h
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h
 create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/statfs.h

diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 0c926050221f4..4715d98bf7556 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -177,6 +177,14 @@ add_proxy_header_library(
     libc.include.llvm-libc-types.struct_stat
 )
 
+add_proxy_header_library(
+  struct_statfs
+  HDRS
+    struct_statfs.h
+  FULL_BUILD_DEPENDS
+    libc.include.llvm-libc-types.struct_statfs
+)
+
 add_proxy_header_library(
   struct_winsize
   HDRS
diff --git a/libc/hdr/types/struct_statfs.h b/libc/hdr/types/struct_statfs.h
new file mode 100644
index 0000000000000..e19d52add80e4
--- /dev/null
+++ b/libc/hdr/types/struct_statfs.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 header for struct statfs.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_STRUCT_STATFS_H
+#define LLVM_LIBC_HDR_TYPES_STRUCT_STATFS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/struct_statfs.h"
+
+#else // Overlay mode
+
+#include <sys/statfs.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_STRUCT_STATFS_H
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index aae93bd905e36..edc36c34932aa 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -307,6 +307,16 @@ DEPENDS
   .fsblkcnt_t
   .fsfilcnt_t
 )
+add_header(fsid_t HDR fsid_t.h)
+add_header(
+  struct_statfs
+HDR
+  struct_statfs.h
+DEPENDS
+  .fsblkcnt_t
+  .fsfilcnt_t
+  .fsid_t
+)
 add_header(locale_t HDR locale_t.h)
 add_header(struct_lconv HDR struct_lconv.h)
 add_header(int_hk_t HDR int_hk_t.h)
diff --git a/libc/include/llvm-libc-types/fsid_t.h b/libc/include/llvm-libc-types/fsid_t.h
new file mode 100644
index 0000000000000..f7fd52e976bbd
--- /dev/null
+++ b/libc/include/llvm-libc-types/fsid_t.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 fsid_t type.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_FSID_T_H
+#define LLVM_LIBC_TYPES_FSID_T_H
+
+typedef struct {
+  int __val[2];
+} fsid_t;
+
+#endif // LLVM_LIBC_TYPES_FSID_T_H
diff --git a/libc/include/llvm-libc-types/struct_statfs.h b/libc/include/llvm-libc-types/struct_statfs.h
new file mode 100644
index 0000000000000..e5acd01267238
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_statfs.h
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 type struct statfs.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_STRUCT_STATFS_H
+#define LLVM_LIBC_TYPES_STRUCT_STATFS_H
+
+#include "fsblkcnt_t.h"
+#include "fsfilcnt_t.h"
+#include "fsid_t.h"
+
+struct statfs {
+  unsigned long f_type;
+  unsigned long f_bsize;
+  fsblkcnt_t f_blocks;
+  fsblkcnt_t f_bfree;
+  fsblkcnt_t f_bavail;
+  fsfilcnt_t f_files;
+  fsfilcnt_t f_ffree;
+  fsid_t f_fsid;
+  unsigned long f_namelen;
+  unsigned long f_frsize;
+  unsigned long f_flags;
+  unsigned long f_spare[4];
+};
+
+#endif // LLVM_LIBC_TYPES_STRUCT_STATFS_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 7d5f94513d0de..a45641483bbef 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -901,3 +901,29 @@ add_header_library(
     libc.src.__support.macros.config
     libc.include.sys_syscall
 )
+
+add_header_library(
+  statfs
+  HDRS
+    statfs.h
+  DEPENDS
+    libc.hdr.types.struct_statfs
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.include.sys_syscall
+)
+
+add_header_library(
+  fstatfs
+  HDRS
+    fstatfs.h
+  DEPENDS
+    libc.hdr.types.struct_statfs
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.error_or
+    libc.src.__support.macros.config
+    libc.include.sys_syscall
+)
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h
new file mode 100644
index 0000000000000..4df0c3628d9dc
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Syscall wrapper for fstatfs.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FSTATFS_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FSTATFS_H
+
+#include "hdr/types/struct_statfs.h"
+#include "src/__support/OSUtil/linux/syscall.h" // For syscall_checked
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> fstatfs(int fd, struct statfs *buf) {
+#ifdef SYS_fstatfs64
+  return syscall_checked<int>(SYS_fstatfs64, fd, sizeof(*buf), buf);
+#else
+  return syscall_checked<int>(SYS_fstatfs, fd, buf);
+#endif
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_FSTATFS_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/statfs.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/statfs.h
new file mode 100644
index 0000000000000..a39db6bf516c4
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/statfs.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Syscall wrapper for statfs.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_STATFS_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_STATFS_H
+
+#include "hdr/types/struct_statfs.h"
+#include "src/__support/OSUtil/linux/syscall.h" // For syscall_checked
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> statfs(const char *path, struct statfs *buf) {
+#ifdef SYS_statfs64
+  return syscall_checked<int>(SYS_statfs64, path, sizeof(*buf), buf);
+#else
+  return syscall_checked<int>(SYS_statfs, path, buf);
+#endif
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_STATFS_H
diff --git a/libc/src/sys/statvfs/linux/CMakeLists.txt b/libc/src/sys/statvfs/linux/CMakeLists.txt
index 2953717e70392..950962c47c941 100644
--- a/libc/src/sys/statvfs/linux/CMakeLists.txt
+++ b/libc/src/sys/statvfs/linux/CMakeLists.txt
@@ -3,11 +3,9 @@ add_header_library(
   HDRS
     statfs_utils.h
   DEPENDS
-    libc.src.errno.errno
-    libc.src.__support.OSUtil.osutil
+    libc.hdr.types.struct_statfs
+    libc.hdr.stdint_proxy
     libc.src.__support.common
-    libc.src.__support.CPP.optional
-    libc.include.sys_syscall
     libc.include.sys_statvfs
 )
 
@@ -19,6 +17,8 @@ add_entrypoint_object(
     ../statvfs.h
   DEPENDS
     libc.src.__support.libc_assert
+    libc.src.errno.errno
+    libc.src.__support.OSUtil.linux.syscall_wrappers.statfs
     libc.include.sys_statvfs
     .statfs_utils
 )
@@ -31,6 +31,8 @@ add_entrypoint_object(
     ../fstatvfs.h
   DEPENDS
     libc.src.__support.libc_assert
+    libc.src.errno.errno
+    libc.src.__support.OSUtil.linux.syscall_wrappers.fstatfs
     libc.include.sys_statvfs
     .statfs_utils
 )
diff --git a/libc/src/sys/statvfs/linux/fstatvfs.cpp b/libc/src/sys/statvfs/linux/fstatvfs.cpp
index 1a2fc04a1dff9..49d4cb818c966 100644
--- a/libc/src/sys/statvfs/linux/fstatvfs.cpp
+++ b/libc/src/sys/statvfs/linux/fstatvfs.cpp
@@ -7,8 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/sys/statvfs/fstatvfs.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_assert.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/sys/statvfs/linux/statfs_utils.h"
 
@@ -16,12 +18,16 @@ namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, fstatvfs, (int fd, struct statvfs *buf)) {
   using namespace statfs_utils;
-  cpp::optional<LinuxStatFs> result = linux_fstatfs(fd);
-  if (result) {
-    LIBC_ASSERT(buf != nullptr);
-    *buf = statfs_to_statvfs(*result);
+  struct statfs result;
+  auto error_or_ret = linux_syscalls::fstatfs(fd, &result);
+  if (!error_or_ret) {
+    libc_errno = error_or_ret.error();
+    return -1;
   }
-  return result ? 0 : -1;
+  result.f_flags &= ~ST_VALID;
+  LIBC_ASSERT(buf != nullptr);
+  *buf = statfs_to_statvfs(result);
+  return 0;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/statvfs/linux/statfs_utils.h b/libc/src/sys/statvfs/linux/statfs_utils.h
index 8ee4de288ef61..90beb1f568c85 100644
--- a/libc/src/sys/statvfs/linux/statfs_utils.h
+++ b/libc/src/sys/statvfs/linux/statfs_utils.h
@@ -9,73 +9,22 @@
 #ifndef LLVM_LIBC_SRC_SYS_STATVFS_LINUX_STATFS_TO_STATVFS_H
 #define LLVM_LIBC_SRC_SYS_STATVFS_LINUX_STATFS_TO_STATVFS_H
 
+#include "hdr/types/struct_statfs.h"
 #include "include/llvm-libc-types/struct_statvfs.h"
-#include "src/__support/CPP/optional.h"
-#include "src/__support/OSUtil/syscall.h"
-#include "src/__support/libc_errno.h"
 #include "src/__support/macros/attributes.h"
 #include "src/__support/macros/config.h"
-#include <asm/statfs.h>
-#include <sys/syscall.h>
-namespace LIBC_NAMESPACE_DECL {
 
+namespace LIBC_NAMESPACE_DECL {
 namespace statfs_utils {
-#ifdef SYS_statfs64
-using LinuxStatFs = statfs64;
-#else
-using LinuxStatFs = statfs;
-#endif
 
 // Linux kernel set an additional flag to f_flags. Libc should mask it out.
-LIBC_INLINE_VAR constexpr decltype(LinuxStatFs::f_flags) ST_VALID = 0x0020;
-
-LIBC_INLINE cpp::optional<LinuxStatFs> linux_statfs(const char *path) {
-  // The kernel syscall routine checks the validity of the path before filling
-  // the statfs structure. So, it is possible that the result is not initialized
-  // after the syscall. Since the struct is trvial, the compiler will generate
-  // pattern filling for the struct.
-  LinuxStatFs result;
-  // On 32-bit platforms, original statfs cannot handle large file systems.
-  // In such cases, SYS_statfs64 is defined and should be used.
-#ifdef SYS_statfs64
-  int ret = syscall_impl<int>(SYS_statfs64, path, sizeof(result), &result);
-#else
-  int ret = syscall_impl<int>(SYS_statfs, path, &result);
-#endif
-  if (ret < 0) {
-    libc_errno = -ret;
-    return cpp::nullopt;
-  }
-  result.f_flags &= ~ST_VALID;
-  return result;
-}
-
-LIBC_INLINE cpp::optional<LinuxStatFs> linux_fstatfs(int fd) {
-  // The kernel syscall routine checks the validity of the path before filling
-  // the statfs structure. So, it is possible that the result is not initialized
-  // after the syscall. Since the struct is trvial, the compiler will generate
-  // pattern filling for the struct.
-  LinuxStatFs result;
-  // On 32-bit platforms, original fstatfs cannot handle large file systems.
-  // In such cases, SYS_fstatfs64 is defined and should be used.
-#ifdef SYS_fstatfs64
-  int ret = syscall_impl<int>(SYS_fstatfs64, fd, sizeof(result), &result);
-#else
-  int ret = syscall_impl<int>(SYS_fstatfs, fd, &result);
-#endif
-  if (ret < 0) {
-    libc_errno = -ret;
-    return cpp::nullopt;
-  }
-  result.f_flags &= ~ST_VALID;
-  return result;
-}
+LIBC_INLINE_VAR constexpr long ST_VALID = 0x0020;
 
 // must use 'struct' tag to refer to type 'statvfs' in this scope. There will be
 // a function in the same namespace with the same name. For consistency, we use
 // struct prefix for all statvfs/statfs related types.
-LIBC_INLINE struct statvfs statfs_to_statvfs(const LinuxStatFs &in) {
-  struct statvfs out;
+LIBC_INLINE struct statvfs statfs_to_statvfs(const struct statfs &in) {
+  struct statvfs out{};
   out.f_bsize = in.f_bsize;
   out.f_frsize = in.f_frsize;
   out.f_blocks = static_cast<decltype(out.f_blocks)>(in.f_blocks);
@@ -84,10 +33,10 @@ LIBC_INLINE struct statvfs statfs_to_statvfs(const LinuxStatFs &in) {
   out.f_files = static_cast<decltype(out.f_files)>(in.f_files);
   out.f_ffree = static_cast<decltype(out.f_ffree)>(in.f_ffree);
   out.f_favail = static_cast<decltype(out.f_favail)>(in.f_ffree);
-  out.f_fsid = in.f_fsid.val[0];
+  out.f_fsid = in.f_fsid.__val[0];
   if constexpr (sizeof(decltype(out.f_fsid)) == sizeof(uint64_t))
-    out.f_fsid |= static_cast<decltype(out.f_fsid)>(in.f_fsid.val[1]) << 32;
-  out.f_flag = in.f_flags;
+    out.f_fsid |= static_cast<decltype(out.f_fsid)>(in.f_fsid.__val[1]) << 32;
+  out.f_flag = in.f_flags & ~ST_VALID;
   out.f_namemax = in.f_namelen;
   return out;
 }
diff --git a/libc/src/sys/statvfs/linux/statvfs.cpp b/libc/src/sys/statvfs/linux/statvfs.cpp
index fc3c75a260ce0..c02de91a26f78 100644
--- a/libc/src/sys/statvfs/linux/statvfs.cpp
+++ b/libc/src/sys/statvfs/linux/statvfs.cpp
@@ -7,8 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/sys/statvfs/statvfs.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/statfs.h"
 #include "src/__support/common.h"
 #include "src/__support/libc_assert.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/sys/statvfs/linux/statfs_utils.h"
 
@@ -18,12 +20,16 @@ LLVM_LIBC_FUNCTION(int, statvfs,
                    (const char *__restrict path,
                     struct statvfs *__restrict buf)) {
   using namespace statfs_utils;
-  cpp::optional<LinuxStatFs> result = linux_statfs(path);
-  if (result) {
-    LIBC_ASSERT(buf != nullptr);
-    *buf = statfs_to_statvfs(*result);
+  struct statfs result;
+  auto error_or_ret = linux_syscalls::statfs(path, &result);
+  if (!error_or_ret) {
+    libc_errno = error_or_ret.error();
+    return -1;
   }
-  return result ? 0 : -1;
+  result.f_flags &= ~ST_VALID;
+  LIBC_ASSERT(buf != nullptr);
+  *buf = statfs_to_statvfs(result);
+  return 0;
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt
index 49520c1dcf265..7e7a551d9539e 100644
--- a/libc/src/unistd/linux/CMakeLists.txt
+++ b/libc/src/unistd/linux/CMakeLists.txt
@@ -156,8 +156,8 @@ add_entrypoint_object(
     ../fpathconf.h
   DEPENDS
     libc.include.unistd
-    libc.include.sys_syscall
-    libc.src.__support.OSUtil.osutil
+    libc.hdr.types.struct_statfs
+    libc.src.__support.OSUtil.linux.syscall_wrappers.fstatfs
     libc.src.errno.errno
     libc.src.unistd.linux.pathconf_utils
 )
@@ -394,8 +394,8 @@ add_entrypoint_object(
     ../pathconf.h
   DEPENDS
     libc.include.unistd
-    libc.include.sys_syscall
-    libc.src.__support.OSUtil.osutil
+    libc.hdr.types.struct_statfs
+    libc.src.__support.OSUtil.linux.syscall_wrappers.statfs
     libc.src.errno.errno
     libc.src.unistd.linux.pathconf_utils
 )
@@ -409,9 +409,8 @@ add_object_library(
   DEPENDS
     libc.hdr.limits_macros
     libc.hdr.unistd_macros
-    libc.src.__support.OSUtil.osutil
     libc.src.errno.errno
-    libc.src.sys.statvfs.linux.statfs_utils
+    libc.hdr.types.struct_statfs
 )
 
 add_entrypoint_object(
diff --git a/libc/src/unistd/linux/fpathconf.cpp b/libc/src/unistd/linux/fpathconf.cpp
index 8e0c8bcdfc22f..1a613d7c820df 100644
--- a/libc/src/unistd/linux/fpathconf.cpp
+++ b/libc/src/unistd/linux/fpathconf.cpp
@@ -7,19 +7,23 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/unistd/fpathconf.h"
-#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "hdr/types/struct_statfs.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h"
 #include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
-#include "src/sys/statvfs/linux/statfs_utils.h"
 #include "src/unistd/linux/pathconf_utils.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(long, fpathconf, (int fd, int name)) {
-  if (cpp::optional<statfs_utils::LinuxStatFs> result =
-          statfs_utils::linux_fstatfs(fd))
-    return pathconfig(result.value(), name);
-  return -1;
+  struct statfs result;
+  auto error_or_ret = linux_syscalls::fstatfs(fd, &result);
+  if (!error_or_ret) {
+    libc_errno = error_or_ret.error();
+    return -1;
+  }
+  return pathconfig(result, name);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/unistd/linux/pathconf.cpp b/libc/src/unistd/linux/pathconf.cpp
index 7dde857c1cfd8..d3146882831da 100644
--- a/libc/src/unistd/linux/pathconf.cpp
+++ b/libc/src/unistd/linux/pathconf.cpp
@@ -7,18 +7,22 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/unistd/pathconf.h"
+#include "hdr/types/struct_statfs.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/statfs.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
-#include "src/sys/statvfs/linux/statfs_utils.h"
 #include "src/unistd/linux/pathconf_utils.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(long, pathconf, (const char *path, int name)) {
-  if (cpp::optional<statfs_utils::LinuxStatFs> result =
-          statfs_utils::linux_statfs(path))
-    return pathconfig(result.value(), name);
-  return -1;
+  struct statfs result;
+  auto error_or_ret = linux_syscalls::statfs(path, &result);
+  if (!error_or_ret) {
+    libc_errno = error_or_ret.error();
+    return -1;
+  }
+  return pathconfig(result, name);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/unistd/linux/pathconf_utils.cpp b/libc/src/unistd/linux/pathconf_utils.cpp
index 9a62e31fd1880..511be724f8862 100644
--- a/libc/src/unistd/linux/pathconf_utils.cpp
+++ b/libc/src/unistd/linux/pathconf_utils.cpp
@@ -12,11 +12,10 @@
 
 #include "hdr/limits_macros.h"
 #include "hdr/unistd_macros.h"
-#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
 #include "src/__support/common.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
-#include "src/sys/statvfs/linux/statfs_utils.h"
+#include "src/unistd/linux/pathconf_utils.h"
 
 // other linux specific includes
 #include <linux/bfs_fs.h>
@@ -30,7 +29,7 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-long filesizebits(const statfs_utils::LinuxStatFs &s) {
+long filesizebits(const ::statfs &s) {
   switch (s.f_type) {
   case JFFS2_SUPER_MAGIC:
   case MSDOS_SUPER_MAGIC:
@@ -40,7 +39,7 @@ long filesizebits(const statfs_utils::LinuxStatFs &s) {
   return 64;
 }
 
-long link_max(const statfs_utils::LinuxStatFs &s) {
+long link_max(const ::statfs &s) {
   switch (s.f_type) {
   case EXT2_SUPER_MAGIC:
     return 32000;
@@ -56,7 +55,7 @@ long link_max(const statfs_utils::LinuxStatFs &s) {
   return LINK_MAX;
 }
 
-long symlinks(const statfs_utils::LinuxStatFs &s) {
+long symlinks(const ::statfs &s) {
   switch (s.f_type) {
   case ADFS_SUPER_MAGIC:
   case BFS_MAGIC:
@@ -69,7 +68,7 @@ long symlinks(const statfs_utils::LinuxStatFs &s) {
   return 1;
 }
 
-long pathconfig(const statfs_utils::LinuxStatFs &s, int name) {
+long pathconfig(const ::statfs &s, int name) {
   switch (name) {
   case _PC_LINK_MAX:
     return link_max(s);
diff --git a/libc/src/unistd/linux/pathconf_utils.h b/libc/src/unistd/linux/pathconf_utils.h
index 8487802c09dca..4302b391e9587 100644
--- a/libc/src/unistd/linux/pathconf_utils.h
+++ b/libc/src/unistd/linux/pathconf_utils.h
@@ -9,12 +9,12 @@
 #ifndef LLVM_LIBC_SRC_UNISTD_PATHCONF_UTILS_H
 #define LLVM_LIBC_SRC_UNISTD_PATHCONF_UTILS_H
 
+#include "hdr/types/struct_statfs.h"
 #include "src/__support/macros/config.h"
-#include "src/sys/statvfs/linux/statfs_utils.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
-long pathconfig(const statfs_utils::LinuxStatFs &s, int name);
+long pathconfig(const ::statfs &s, int name);
 
 } // namespace LIBC_NAMESPACE_DECL
 

>From e5dd0731ea8ec9a48f1b0c72f8a96dcbddb43c42 Mon Sep 17 00:00:00 2001
From: Alexey Samsonov <vonosmas at gmail.com>
Date: Thu, 30 Jul 2026 05:27:01 +0000
Subject: [PATCH 2/4] slight fix for statvfs/fstatvfs entrypoints

---
 libc/src/sys/statvfs/linux/CMakeLists.txt | 2 ++
 libc/src/sys/statvfs/linux/fstatvfs.cpp   | 7 +++----
 libc/src/sys/statvfs/linux/statvfs.cpp    | 7 +++----
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libc/src/sys/statvfs/linux/CMakeLists.txt b/libc/src/sys/statvfs/linux/CMakeLists.txt
index 950962c47c941..a00d5a039406b 100644
--- a/libc/src/sys/statvfs/linux/CMakeLists.txt
+++ b/libc/src/sys/statvfs/linux/CMakeLists.txt
@@ -18,6 +18,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.libc_assert
     libc.src.errno.errno
+    libc.src.__support.macros.null_check
     libc.src.__support.OSUtil.linux.syscall_wrappers.statfs
     libc.include.sys_statvfs
     .statfs_utils
@@ -32,6 +33,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.libc_assert
     libc.src.errno.errno
+    libc.src.__support.macros.null_check
     libc.src.__support.OSUtil.linux.syscall_wrappers.fstatfs
     libc.include.sys_statvfs
     .statfs_utils
diff --git a/libc/src/sys/statvfs/linux/fstatvfs.cpp b/libc/src/sys/statvfs/linux/fstatvfs.cpp
index 49d4cb818c966..6f3d8b0714b83 100644
--- a/libc/src/sys/statvfs/linux/fstatvfs.cpp
+++ b/libc/src/sys/statvfs/linux/fstatvfs.cpp
@@ -12,21 +12,20 @@
 #include "src/__support/libc_assert.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
 #include "src/sys/statvfs/linux/statfs_utils.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, fstatvfs, (int fd, struct statvfs *buf)) {
-  using namespace statfs_utils;
+  LIBC_CRASH_ON_NULLPTR(buf);
   struct statfs result;
   auto error_or_ret = linux_syscalls::fstatfs(fd, &result);
   if (!error_or_ret) {
     libc_errno = error_or_ret.error();
     return -1;
   }
-  result.f_flags &= ~ST_VALID;
-  LIBC_ASSERT(buf != nullptr);
-  *buf = statfs_to_statvfs(result);
+  *buf = statfs_utils::statfs_to_statvfs(result);
   return 0;
 }
 
diff --git a/libc/src/sys/statvfs/linux/statvfs.cpp b/libc/src/sys/statvfs/linux/statvfs.cpp
index c02de91a26f78..75cd54d22e389 100644
--- a/libc/src/sys/statvfs/linux/statvfs.cpp
+++ b/libc/src/sys/statvfs/linux/statvfs.cpp
@@ -12,6 +12,7 @@
 #include "src/__support/libc_assert.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
 #include "src/sys/statvfs/linux/statfs_utils.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -19,16 +20,14 @@ namespace LIBC_NAMESPACE_DECL {
 LLVM_LIBC_FUNCTION(int, statvfs,
                    (const char *__restrict path,
                     struct statvfs *__restrict buf)) {
-  using namespace statfs_utils;
+  LIBC_CRASH_ON_NULLPTR(buf);
   struct statfs result;
   auto error_or_ret = linux_syscalls::statfs(path, &result);
   if (!error_or_ret) {
     libc_errno = error_or_ret.error();
     return -1;
   }
-  result.f_flags &= ~ST_VALID;
-  LIBC_ASSERT(buf != nullptr);
-  *buf = statfs_to_statvfs(result);
+  *buf = statfs_utils::statfs_to_statvfs(result);
   return 0;
 }
 

>From 455c9bf1adf310de9b7bd50ac7466a0573214e20 Mon Sep 17 00:00:00 2001
From: Alexey Samsonov <vonosmas at gmail.com>
Date: Thu, 30 Jul 2026 05:39:03 +0000
Subject: [PATCH 3/4] Use 'struct statfs' properly

---
 libc/src/unistd/linux/pathconf_utils.cpp | 8 ++++----
 libc/src/unistd/linux/pathconf_utils.h   | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/libc/src/unistd/linux/pathconf_utils.cpp b/libc/src/unistd/linux/pathconf_utils.cpp
index 511be724f8862..52e59c3794ffd 100644
--- a/libc/src/unistd/linux/pathconf_utils.cpp
+++ b/libc/src/unistd/linux/pathconf_utils.cpp
@@ -29,7 +29,7 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-long filesizebits(const ::statfs &s) {
+long filesizebits(const struct statfs &s) {
   switch (s.f_type) {
   case JFFS2_SUPER_MAGIC:
   case MSDOS_SUPER_MAGIC:
@@ -39,7 +39,7 @@ long filesizebits(const ::statfs &s) {
   return 64;
 }
 
-long link_max(const ::statfs &s) {
+long link_max(const struct statfs &s) {
   switch (s.f_type) {
   case EXT2_SUPER_MAGIC:
     return 32000;
@@ -55,7 +55,7 @@ long link_max(const ::statfs &s) {
   return LINK_MAX;
 }
 
-long symlinks(const ::statfs &s) {
+long symlinks(const struct statfs &s) {
   switch (s.f_type) {
   case ADFS_SUPER_MAGIC:
   case BFS_MAGIC:
@@ -68,7 +68,7 @@ long symlinks(const ::statfs &s) {
   return 1;
 }
 
-long pathconfig(const ::statfs &s, int name) {
+long pathconfig(const struct statfs &s, int name) {
   switch (name) {
   case _PC_LINK_MAX:
     return link_max(s);
diff --git a/libc/src/unistd/linux/pathconf_utils.h b/libc/src/unistd/linux/pathconf_utils.h
index 4302b391e9587..b1fd3db5a99e6 100644
--- a/libc/src/unistd/linux/pathconf_utils.h
+++ b/libc/src/unistd/linux/pathconf_utils.h
@@ -14,7 +14,7 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-long pathconfig(const ::statfs &s, int name);
+long pathconfig(const struct statfs &s, int name);
 
 } // namespace LIBC_NAMESPACE_DECL
 

>From 486e10d173929acb6b28d4db582cbb3c71182201 Mon Sep 17 00:00:00 2001
From: Alexey Samsonov <vonosmas at gmail.com>
Date: Thu, 30 Jul 2026 14:18:18 +0000
Subject: [PATCH 4/4] Add asserts and comments per review feedback.

---
 libc/include/llvm-libc-types/struct_statfs.h               | 3 +++
 libc/src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h | 5 +++++
 libc/src/__support/OSUtil/linux/syscall_wrappers/statfs.h  | 5 +++++
 3 files changed, 13 insertions(+)

diff --git a/libc/include/llvm-libc-types/struct_statfs.h b/libc/include/llvm-libc-types/struct_statfs.h
index e5acd01267238..2767d44507177 100644
--- a/libc/include/llvm-libc-types/struct_statfs.h
+++ b/libc/include/llvm-libc-types/struct_statfs.h
@@ -18,6 +18,9 @@
 #include "fsfilcnt_t.h"
 #include "fsid_t.h"
 
+// NOTE: This structure may have different layouts on architectures we don't
+// fully support (e.g. s390 or MIPS).
+
 struct statfs {
   unsigned long f_type;
   unsigned long f_bsize;
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h
index 4df0c3628d9dc..39c48bd012060 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/fstatfs.h
@@ -26,8 +26,13 @@ namespace linux_syscalls {
 
 LIBC_INLINE ErrorOr<int> fstatfs(int fd, struct statfs *buf) {
 #ifdef SYS_fstatfs64
+  static_assert(sizeof(statfs::f_blocks) == 8,
+                "Can only be used with 64-bit version of the struct");
   return syscall_checked<int>(SYS_fstatfs64, fd, sizeof(*buf), buf);
 #else
+  static_assert(
+      sizeof(statfs::f_blocks) == sizeof(long),
+      "The fallback is unsafe on 32-bit platforms with 64-bit f_blocks.");
   return syscall_checked<int>(SYS_fstatfs, fd, buf);
 #endif
 }
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/statfs.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/statfs.h
index a39db6bf516c4..1c1cbd4ec4ee0 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/statfs.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/statfs.h
@@ -26,8 +26,13 @@ namespace linux_syscalls {
 
 LIBC_INLINE ErrorOr<int> statfs(const char *path, struct statfs *buf) {
 #ifdef SYS_statfs64
+  static_assert(sizeof(statfs::f_blocks) == 8,
+                "Can only be used with 64-bit version of the struct");
   return syscall_checked<int>(SYS_statfs64, path, sizeof(*buf), buf);
 #else
+  static_assert(
+      sizeof(statfs::f_blocks) == sizeof(long),
+      "The fallback is unsafe on 32-bit platforms with 64-bit f_blocks.");
   return syscall_checked<int>(SYS_statfs, path, buf);
 #endif
 }



More information about the libc-commits mailing list