[libc-commits] [libc] [libc] add statvfs/fstatvfs (PR #86169)

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Sun Mar 24 12:34:10 PDT 2024


https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/86169

>From 182ef6932ac2261749b8ed3eaebed8a88f91494c Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Wed, 20 Mar 2024 11:48:54 -0400
Subject: [PATCH 1/8] [libc] add statvfs

---
 libc/config/linux/api.td                      |  4 +
 libc/config/linux/x86_64/entrypoints.txt      |  4 +
 libc/include/CMakeLists.txt                   |  9 +++
 libc/include/llvm-libc-types/CMakeLists.txt   | 10 +++
 libc/include/llvm-libc-types/fsblkcnt_t.h     | 14 ++++
 libc/include/llvm-libc-types/fsfilcnt_t.h     | 14 ++++
 libc/include/llvm-libc-types/struct_statvfs.h | 26 ++++++
 libc/include/sys/statvfs.h.def                | 16 ++++
 libc/spec/posix.td                            | 30 +++++++
 libc/src/sys/CMakeLists.txt                   |  1 +
 libc/src/sys/statvfs/CMakeLists.txt           | 17 ++++
 libc/src/sys/statvfs/fstatvfs.h               | 20 +++++
 libc/src/sys/statvfs/linux/CMakeLists.txt     | 34 ++++++++
 libc/src/sys/statvfs/linux/fstatvfs.cpp       | 24 ++++++
 libc/src/sys/statvfs/linux/statfs_utils.h     | 80 +++++++++++++++++++
 libc/src/sys/statvfs/linux/statvfs.cpp        | 26 ++++++
 libc/src/sys/statvfs/statvfs.h                | 20 +++++
 libc/test/src/sys/CMakeLists.txt              |  1 +
 libc/test/src/sys/statvfs/CMakeLists.txt      |  3 +
 .../test/src/sys/statvfs/linux/CMakeLists.txt | 30 +++++++
 .../src/sys/statvfs/linux/fstatvfs_test.cpp   | 47 +++++++++++
 .../src/sys/statvfs/linux/statvfs_test.cpp    | 51 ++++++++++++
 22 files changed, 481 insertions(+)
 create mode 100644 libc/include/llvm-libc-types/fsblkcnt_t.h
 create mode 100644 libc/include/llvm-libc-types/fsfilcnt_t.h
 create mode 100644 libc/include/llvm-libc-types/struct_statvfs.h
 create mode 100644 libc/include/sys/statvfs.h.def
 create mode 100644 libc/src/sys/statvfs/CMakeLists.txt
 create mode 100644 libc/src/sys/statvfs/fstatvfs.h
 create mode 100644 libc/src/sys/statvfs/linux/CMakeLists.txt
 create mode 100644 libc/src/sys/statvfs/linux/fstatvfs.cpp
 create mode 100644 libc/src/sys/statvfs/linux/statfs_utils.h
 create mode 100644 libc/src/sys/statvfs/linux/statvfs.cpp
 create mode 100644 libc/src/sys/statvfs/statvfs.h
 create mode 100644 libc/test/src/sys/statvfs/CMakeLists.txt
 create mode 100644 libc/test/src/sys/statvfs/linux/CMakeLists.txt
 create mode 100644 libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
 create mode 100644 libc/test/src/sys/statvfs/linux/statvfs_test.cpp

diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index e9e82c5d478945..f4d4164d4d1986 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -262,3 +262,7 @@ def SetJmpAPI : PublicAPI<"setjmp.h"> {
 def SearchAPI : PublicAPI<"search.h"> {
   let Types = ["ACTION", "ENTRY", "struct hsearch_data"];
 }
+
+def SysStatvfsAPI : PublicAPI<"sys/statvfs.h"> {
+  let Types = ["struct statvfs"];
+}
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index e8cf11266624a7..482f444677f608 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -253,6 +253,10 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.stat.mkdirat
     libc.src.sys.stat.stat
 
+    # sys/statvfs.h
+    libc.src.sys.statvfs.statvfs
+    libc.src.sys.statvfs.fstatvfs
+
     # sys/utsname.h entrypoints
     libc.src.sys.utsname.uname
 
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index b2cb10459c53e3..4203f0bc901b22 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -498,6 +498,15 @@ add_gen_header(
     .llvm-libc-types.struct_sockaddr_un
 )
 
+add_gen_header(
+  sys_statvfs
+  DEF_FILE sys/statvfs.h.def
+  GEN_HDR sys/statvfs.h
+  DEPENDS
+    .llvm_libc_common_h
+    .llvm-libc-types.struct_statvfs
+)
+
 add_gen_header(
   sys_syscall
   DEF_FILE sys/syscall.h.def
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 7fef976d7b3250..93a79e1477b337 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -106,3 +106,13 @@ add_header(
   DEPENDS
     libc.include.llvm-libc-macros.float_macros
 )
+add_header(fsblkcnt_t HDR fsblkcnt_t.h)
+add_header(fsfilcnt_t HDR fsfilcnt_t.h)
+add_header(
+  struct_statvfs 
+HDR 
+  struct_statvfs.h
+DEPENDS
+  .fsblkcnt_t
+  .fsfilcnt_t
+)
diff --git a/libc/include/llvm-libc-types/fsblkcnt_t.h b/libc/include/llvm-libc-types/fsblkcnt_t.h
new file mode 100644
index 00000000000000..88a53d38cb1b3c
--- /dev/null
+++ b/libc/include/llvm-libc-types/fsblkcnt_t.h
@@ -0,0 +1,14 @@
+//===-- Definition of fsblkcnt_t type -------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_FSBLKCNT_T_H
+#define LLVM_LIBC_TYPES_FSBLKCNT_T_H
+
+typedef __SIZE_TYPE__ fsblkcnt_t;
+
+#endif // LLVM_LIBC_TYPES_FSBLKCNT_T_H
diff --git a/libc/include/llvm-libc-types/fsfilcnt_t.h b/libc/include/llvm-libc-types/fsfilcnt_t.h
new file mode 100644
index 00000000000000..c5693591907ab7
--- /dev/null
+++ b/libc/include/llvm-libc-types/fsfilcnt_t.h
@@ -0,0 +1,14 @@
+//===-- Definition of fsfilcnt_t type -------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_FSFILCNT_T_H
+#define LLVM_LIBC_TYPES_FSFILCNT_T_H
+
+typedef __SIZE_TYPE__ fsfilcnt_t;
+
+#endif // LLVM_LIBC_TYPES_FSFILCNT_T_H
diff --git a/libc/include/llvm-libc-types/struct_statvfs.h b/libc/include/llvm-libc-types/struct_statvfs.h
new file mode 100644
index 00000000000000..265896e9b3a469
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_statvfs.h
@@ -0,0 +1,26 @@
+//===-- Definition of type struct statvfs ---------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TYPES_STRUCT_STATVFS_H
+#define LLVM_LIBC_TYPES_STRUCT_STATVFS_H
+#include <llvm-libc-types/fsblkcnt_t.h>
+#include <llvm-libc-types/fsfilcnt_t.h>
+struct statvfs {
+  unsigned long f_bsize;   /* Filesystem block size */
+  unsigned long f_frsize;  /* Fragment size */
+  fsblkcnt_t f_blocks;     /* Size of fs in f_frsize units */
+  fsblkcnt_t f_bfree;      /* Number of free blocks */
+  fsblkcnt_t f_bavail;     /* Number of free blocks for unprivileged users */
+  fsfilcnt_t f_files;      /* Number of inodes */
+  fsfilcnt_t f_ffree;      /* Number of free inodes */
+  fsfilcnt_t f_favail;     /* Number of free inodes for unprivileged users */
+  unsigned long f_fsid;    /* Filesystem ID */
+  unsigned long f_flag;    /* Mount flags */
+  unsigned long f_namemax; /* Maximum filename length */
+};
+#endif // LLVM_LIBC_TYPES_STRUCT_STATVFS_H
diff --git a/libc/include/sys/statvfs.h.def b/libc/include/sys/statvfs.h.def
new file mode 100644
index 00000000000000..f23c9a3d5b1f93
--- /dev/null
+++ b/libc/include/sys/statvfs.h.def
@@ -0,0 +1,16 @@
+//===-- POSIX header statvfs.h --------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SYS_STATVFS_H
+#define LLVM_LIBC_SYS_STATVFS_H
+
+#include <__llvm-libc-common.h>
+
+%%public_api()
+
+#endif // LLVM_LIBC_SYS_STATVFS_H
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index 3b793ea90ffd32..8444a449ebe5be 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -86,6 +86,10 @@ def ConstStructSockAddrPtr : ConstType<StructSockAddrPtr>;
 
 def StructSockAddrUn : NamedType<"struct sockaddr_un">;
 
+def StructStatvfs : NamedType<"struct statvfs">;
+def StructStatvfsPtr : PtrType<StructStatvfs>;
+def RestrictedStructStatvfsPtr : RestrictedPtrType<StructStatvfs>;
+
 def POSIX : StandardSpec<"POSIX"> {
   PtrType CharPtr = PtrType<CharType>;
   RestrictedPtrType RestrictedCharPtr = RestrictedPtrType<CharType>;
@@ -888,6 +892,31 @@ def POSIX : StandardSpec<"POSIX"> {
     ]
   >;
 
+  HeaderSpec SysStatvfs = HeaderSpec<
+      "sys/statvfs.h",
+      [], // Macros
+      [StructStatvfs], // Types
+      [], // Enumerations
+      [
+        FunctionSpec<
+          "statvfs",
+          RetValSpec<IntType>,
+          [
+            ArgSpec<ConstRestrictedCharPtr>,
+            ArgSpec<RestrictedStructStatvfsPtr>
+          ]
+        >,
+        FunctionSpec<
+          "fstatvfs",
+          RetValSpec<IntType>,
+          [
+            ArgSpec<IntType>,
+            ArgSpec<StructStatvfsPtr>
+          ]
+        >,
+      ]  // Functions
+    >;
+
   NamedType StructUtsName = NamedType<"struct utsname">;
   PtrType StructUtsNamePtr = PtrType<StructUtsName>;
   HeaderSpec SysUtsName = HeaderSpec<
@@ -1505,6 +1534,7 @@ def POSIX : StandardSpec<"POSIX"> {
     SysSelect,
     SysSocket,
     SysStat,
+    SysStatvfs,
     SysTypes,
     SysUtsName,
     SysWait,
diff --git a/libc/src/sys/CMakeLists.txt b/libc/src/sys/CMakeLists.txt
index 57ea7b4beaca1b..adc666b94202f7 100644
--- a/libc/src/sys/CMakeLists.txt
+++ b/libc/src/sys/CMakeLists.txt
@@ -7,6 +7,7 @@ add_subdirectory(select)
 add_subdirectory(socket)
 add_subdirectory(sendfile)
 add_subdirectory(stat)
+add_subdirectory(statvfs)
 add_subdirectory(utsname)
 add_subdirectory(wait)
 add_subdirectory(prctl)
diff --git a/libc/src/sys/statvfs/CMakeLists.txt b/libc/src/sys/statvfs/CMakeLists.txt
new file mode 100644
index 00000000000000..6a10187a135a5c
--- /dev/null
+++ b/libc/src/sys/statvfs/CMakeLists.txt
@@ -0,0 +1,17 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+  add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+endif()
+
+add_entrypoint_object(
+  statvfs
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.statvfs
+)
+
+add_entrypoint_object(
+  fstatvfs
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.fstatvfs
+)
diff --git a/libc/src/sys/statvfs/fstatvfs.h b/libc/src/sys/statvfs/fstatvfs.h
new file mode 100644
index 00000000000000..6ca76a459ae5bc
--- /dev/null
+++ b/libc/src/sys/statvfs/fstatvfs.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for fstatvfs ----------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_STATVFS_FSTATVFS_H
+#define LLVM_LIBC_SRC_SYS_STATVFS_FSTATVFS_H
+
+#include "llvm-libc-types/struct_statvfs.h"
+
+namespace LIBC_NAMESPACE {
+
+int fstatvfs(int fd, struct statvfs *buf);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SYS_STATVFS_FSTATVFS_H
diff --git a/libc/src/sys/statvfs/linux/CMakeLists.txt b/libc/src/sys/statvfs/linux/CMakeLists.txt
new file mode 100644
index 00000000000000..c481b07005728a
--- /dev/null
+++ b/libc/src/sys/statvfs/linux/CMakeLists.txt
@@ -0,0 +1,34 @@
+add_header_library(
+  statfs_utils
+  HDRS
+    statfs_utils.h
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.__support.OSUtil.osutil
+    libc.src.__support.common
+    libc.src.__support.CPP.optional
+    libc.include.sys.syscall
+)
+
+add_entrypoint_object(
+  statvfs
+  SRCS
+    statvfs.cpp
+  HDRS
+    ../statvfs.h
+  DEPENDS
+    libc.include.llvm-libc-types.struct_statvfs
+    .statfs_utils
+)
+
+add_entrypoint_object(
+  fstatvfs
+  SRCS
+    fstatvfs.cpp
+  HDRS
+    ../fstatvfs.h
+  DEPENDS
+    libc.include.llvm-libc-types.struct_statvfs
+    .statfs_utils
+)
+
diff --git a/libc/src/sys/statvfs/linux/fstatvfs.cpp b/libc/src/sys/statvfs/linux/fstatvfs.cpp
new file mode 100644
index 00000000000000..03543f68f1efe3
--- /dev/null
+++ b/libc/src/sys/statvfs/linux/fstatvfs.cpp
@@ -0,0 +1,24 @@
+//===-- Linux implementation of fstatvfs ----------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/statvfs/fstatvfs.h"
+#include "src/__support/common.h"
+#include "src/sys/statvfs/linux/statfs_utils.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, fstatvfs, (int fd, struct statvfs *buf)) {
+  using namespace statfs_utils;
+  if (cpp::optional<LinuxStatFs> result = linux_fstatfs(fd)) {
+    *buf = statfs_to_statvfs(*result);
+    return 0;
+  }
+  return -1;
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/sys/statvfs/linux/statfs_utils.h b/libc/src/sys/statvfs/linux/statfs_utils.h
new file mode 100644
index 00000000000000..67e026cf8103d9
--- /dev/null
+++ b/libc/src/sys/statvfs/linux/statfs_utils.h
@@ -0,0 +1,80 @@
+//===-- Convert Statfs to Statvfs -------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_STATVFS_LINUX_STATFS_TO_STATVFS_H
+#define LLVM_LIBC_SRC_SYS_STATVFS_LINUX_STATFS_TO_STATVFS_H
+
+#include "llvm-libc-types/struct_statvfs.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/macros/attributes.h"
+#include "src/errno/libc_errno.h"
+#include <asm/statfs.h>
+#include <sys/syscall.h>
+namespace LIBC_NAMESPACE {
+
+namespace statfs_utils {
+#ifdef SYS_statfs64
+using LinuxStatFs = statfs64;
+#else
+using LinuxStatFs = statfs;
+#endif
+
+LIBC_INLINE cpp::optional<LinuxStatFs> linux_statfs(const char *path) {
+  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;
+  }
+  return result;
+}
+
+LIBC_INLINE cpp::optional<LinuxStatFs> linux_fstatfs(int fd) {
+  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;
+  }
+  return result;
+}
+
+// use struct stat(v)fs to avoid conflicts with the function names.
+LIBC_INLINE struct statvfs statfs_to_statvfs(const LinuxStatFs &in) {
+  struct statvfs out;
+  out.f_bsize = in.f_bsize;
+  out.f_frsize = in.f_frsize;
+  out.f_blocks = in.f_blocks;
+  out.f_bfree = in.f_bfree;
+  out.f_bavail = in.f_bavail;
+  out.f_files = in.f_files;
+  out.f_ffree = in.f_ffree;
+  out.f_favail = in.f_ffree;
+  out.f_fsid = in.f_fsid.val[0] |
+               static_cast<decltype(out.f_fsid)>(in.f_fsid.val[1]) << 32;
+  out.f_flag = in.f_flags;
+  out.f_namemax = in.f_namelen;
+  return out;
+}
+} // namespace statfs_utils
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SYS_STATVFS_LINUX_STATFS_TO_STATVFS_H
diff --git a/libc/src/sys/statvfs/linux/statvfs.cpp b/libc/src/sys/statvfs/linux/statvfs.cpp
new file mode 100644
index 00000000000000..c71065930766f2
--- /dev/null
+++ b/libc/src/sys/statvfs/linux/statvfs.cpp
@@ -0,0 +1,26 @@
+//===-- Linux implementation of statvfs -----------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/statvfs/statvfs.h"
+#include "src/__support/common.h"
+#include "src/sys/statvfs/linux/statfs_utils.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, statvfs,
+                   (const char *__restrict path,
+                    struct statvfs *__restrict buf)) {
+  using namespace statfs_utils;
+  if (cpp::optional<LinuxStatFs> result = linux_statfs(path)) {
+    *buf = statfs_to_statvfs(*result);
+    return 0;
+  }
+  return -1;
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/sys/statvfs/statvfs.h b/libc/src/sys/statvfs/statvfs.h
new file mode 100644
index 00000000000000..792c7ddd0164ec
--- /dev/null
+++ b/libc/src/sys/statvfs/statvfs.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for statvfs -----------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_STATVFS_STATVFS_H
+#define LLVM_LIBC_SRC_SYS_STATVFS_STATVFS_H
+
+#include "llvm-libc-types/struct_statvfs.h"
+
+namespace LIBC_NAMESPACE {
+
+int statvfs(const char *__restrict path, struct statvfs *__restrict buf);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SYS_STATVFS_STATVFS_H
diff --git a/libc/test/src/sys/CMakeLists.txt b/libc/test/src/sys/CMakeLists.txt
index 7f228e709046d0..dc0aa8bf7b75dc 100644
--- a/libc/test/src/sys/CMakeLists.txt
+++ b/libc/test/src/sys/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(select)
 add_subdirectory(sendfile)
 add_subdirectory(socket)
 add_subdirectory(stat)
+add_subdirectory(statvfs)
 add_subdirectory(utsname)
 add_subdirectory(wait)
 add_subdirectory(prctl)
diff --git a/libc/test/src/sys/statvfs/CMakeLists.txt b/libc/test/src/sys/statvfs/CMakeLists.txt
new file mode 100644
index 00000000000000..b4bbe81c92ff2e
--- /dev/null
+++ b/libc/test/src/sys/statvfs/CMakeLists.txt
@@ -0,0 +1,3 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+  add_subdirectory(${LIBC_TARGET_OS})
+endif()
diff --git a/libc/test/src/sys/statvfs/linux/CMakeLists.txt b/libc/test/src/sys/statvfs/linux/CMakeLists.txt
new file mode 100644
index 00000000000000..0a695bce968435
--- /dev/null
+++ b/libc/test/src/sys/statvfs/linux/CMakeLists.txt
@@ -0,0 +1,30 @@
+add_custom_target(libc_sys_statvfs_unittests)
+
+add_libc_unittest(
+  statvfs_test
+  SUITE
+    libc_sys_statvfs_unittests
+  SRCS
+    statvfs_test.cpp
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.sys.statvfs.linux.statfs_utils
+    libc.src.sys.statvfs.statvfs
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
+add_libc_unittest(
+  fstatvfs_test
+  SUITE
+    libc_sys_statvfs_unittests
+  SRCS
+    fstatvfs_test.cpp
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.sys.statvfs.linux.statfs_utils
+    libc.src.sys.statvfs.fstatvfs
+    libc.src.fcntl.open
+    libc.src.unistd.close
+    libc.src.__support.CPP.string_view
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
diff --git a/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp b/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
new file mode 100644
index 00000000000000..7a8ca8680978a2
--- /dev/null
+++ b/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
@@ -0,0 +1,47 @@
+#include "src/__support/CPP/string_view.h"
+#include "src/fcntl/open.h"
+#include "src/sys/statvfs/fstatvfs.h"
+#include "src/sys/statvfs/linux/statfs_utils.h"
+#include "src/unistd/close.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/LibcTest.h"
+#include <linux/magic.h>
+#include <llvm-libc-macros/linux/fcntl-macros.h>
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+
+namespace LIBC_NAMESPACE {
+static int fstatfs(int fd, struct statfs *buf) {
+  using namespace statfs_utils;
+  if (cpp::optional<LinuxStatFs> result = linux_fstatfs(fd)) {
+    *buf = *result;
+    return 0;
+  }
+  return -1;
+}
+} // namespace LIBC_NAMESPACE
+
+struct PathFD {
+  int fd;
+  explicit PathFD(LIBC_NAMESPACE::cpp::string_view path)
+      : fd(LIBC_NAMESPACE::open(path.data(), O_CLOEXEC | O_PATH)) {}
+  ~PathFD() { LIBC_NAMESPACE::close(fd); }
+  operator int() const { return fd; }
+};
+
+TEST(LlvmLibcSysStatfsTest, FstatfsBasic) {
+  statfs buf[1];
+  ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/"), buf), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/proc"), buf), Succeeds());
+  ASSERT_EQ(buf->f_type, static_cast<decltype(buf->f_type)>(PROC_SUPER_MAGIC));
+  ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/sys"), buf), Succeeds());
+  ASSERT_EQ(buf->f_type, static_cast<decltype(buf->f_type)>(SYSFS_MAGIC));
+}
+
+TEST(LlvmLibcSysStatfsTest, FstatfsNullBuffer) {
+  ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(PathFD("/"), nullptr), Fails(EFAULT));
+}
+
+TEST(LlvmLibcSysStatfsTest, FstatfsInvalidFD) {
+  statvfs buf[1];
+  ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(-1, buf), Fails(EBADF));
+}
diff --git a/libc/test/src/sys/statvfs/linux/statvfs_test.cpp b/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
new file mode 100644
index 00000000000000..116d32ecb09db6
--- /dev/null
+++ b/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
@@ -0,0 +1,51 @@
+#include "src/sys/statvfs/linux/statfs_utils.h"
+#include "src/sys/statvfs/statvfs.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/LibcTest.h"
+#include <linux/magic.h>
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+
+namespace LIBC_NAMESPACE {
+static int statfs(const char *path, struct statfs *buf) {
+  using namespace statfs_utils;
+  if (cpp::optional<LinuxStatFs> result = linux_statfs(path)) {
+    *buf = *result;
+    return 0;
+  }
+  return -1;
+}
+} // namespace LIBC_NAMESPACE
+
+TEST(LlvmLibcSysStatfsTest, StatfsBasic) {
+  statfs buf[1];
+  ASSERT_THAT(LIBC_NAMESPACE::statfs("/", buf), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::statfs("/proc", buf), Succeeds());
+  ASSERT_EQ(buf->f_type, static_cast<__kernel_long_t>(PROC_SUPER_MAGIC));
+  ASSERT_THAT(LIBC_NAMESPACE::statfs("/sys", buf), Succeeds());
+  ASSERT_EQ(buf->f_type, static_cast<__kernel_long_t>(SYSFS_MAGIC));
+}
+
+TEST(LlvmLibcSysStatfsTest, StatfsNullBuffer) {
+  ASSERT_THAT(LIBC_NAMESPACE::statfs("/", nullptr), Fails(EFAULT));
+}
+
+TEST(LlvmLibcSysStatfsTest, InvalidPath) {
+  statvfs buf[1];
+  ASSERT_THAT(LIBC_NAMESPACE::statvfs("", buf), Fails(ENOENT));
+  ASSERT_THAT(LIBC_NAMESPACE::statvfs("/nonexistent", buf), Fails(ENOENT));
+  ASSERT_THAT(LIBC_NAMESPACE::statvfs("/dev/null/whatever", buf),
+              Fails(ENOTDIR));
+  ASSERT_THAT(LIBC_NAMESPACE::statvfs(nullptr, buf), Fails(EFAULT));
+}
+
+TEST(LlvmLibcSysStatfsTest, NameTooLong) {
+  statvfs buf[1];
+  ASSERT_THAT(LIBC_NAMESPACE::statvfs("/", buf), Succeeds());
+  char *name = static_cast<char *>(__builtin_alloca(buf->f_namemax + 3));
+  name[0] = '/';
+  name[buf->f_namemax + 2] = '\0';
+  for (unsigned i = 1; i < buf->f_namemax + 2; ++i) {
+    name[i] = 'a';
+  }
+  ASSERT_THAT(LIBC_NAMESPACE::statvfs(name, buf), Fails(ENAMETOOLONG));
+}

>From 19459451af5bb91ac85b0cfc210e76dc78570726 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Thu, 21 Mar 2024 14:40:42 -0400
Subject: [PATCH 2/8] [libc] add missing types

---
 libc/config/linux/api.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index f4d4164d4d1986..eb5ed8089850e4 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -264,5 +264,5 @@ def SearchAPI : PublicAPI<"search.h"> {
 }
 
 def SysStatvfsAPI : PublicAPI<"sys/statvfs.h"> {
-  let Types = ["struct statvfs"];
+  let Types = ["fsblkcnt_t", "fsfilcnt_t", "struct statvfs"];
 }

>From 1345cee3cf46dd9f8b95a4c0dcf2f3a79d4ba88b Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Thu, 21 Mar 2024 14:43:42 -0400
Subject: [PATCH 3/8] fix tests

---
 libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp | 6 +++---
 libc/test/src/sys/statvfs/linux/statvfs_test.cpp  | 5 +++--
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp b/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
index 7a8ca8680978a2..5899e64fb91f10 100644
--- a/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
+++ b/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
@@ -28,7 +28,7 @@ struct PathFD {
   operator int() const { return fd; }
 };
 
-TEST(LlvmLibcSysStatfsTest, FstatfsBasic) {
+TEST(LlvmLibcSysStatvfsTest, FstatfsBasic) {
   statfs buf[1];
   ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/"), buf), Succeeds());
   ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/proc"), buf), Succeeds());
@@ -37,11 +37,11 @@ TEST(LlvmLibcSysStatfsTest, FstatfsBasic) {
   ASSERT_EQ(buf->f_type, static_cast<decltype(buf->f_type)>(SYSFS_MAGIC));
 }
 
-TEST(LlvmLibcSysStatfsTest, FstatfsNullBuffer) {
+TEST(LlvmLibcSysStatvfsTest, FstatvfsNullBuffer) {
   ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(PathFD("/"), nullptr), Fails(EFAULT));
 }
 
-TEST(LlvmLibcSysStatfsTest, FstatfsInvalidFD) {
+TEST(LlvmLibcSysStatvfsTest, FstatvfsInvalidFD) {
   statvfs buf[1];
   ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(-1, buf), Fails(EBADF));
 }
diff --git a/libc/test/src/sys/statvfs/linux/statvfs_test.cpp b/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
index 116d32ecb09db6..951edfeb22b20c 100644
--- a/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
+++ b/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
@@ -25,11 +25,12 @@ TEST(LlvmLibcSysStatfsTest, StatfsBasic) {
   ASSERT_EQ(buf->f_type, static_cast<__kernel_long_t>(SYSFS_MAGIC));
 }
 
+// POSIX API does not specify what happens when buf is NULL
 TEST(LlvmLibcSysStatfsTest, StatfsNullBuffer) {
   ASSERT_THAT(LIBC_NAMESPACE::statfs("/", nullptr), Fails(EFAULT));
 }
 
-TEST(LlvmLibcSysStatfsTest, InvalidPath) {
+TEST(LlvmLibcSysStatfsTest, StatvfsInvalidPath) {
   statvfs buf[1];
   ASSERT_THAT(LIBC_NAMESPACE::statvfs("", buf), Fails(ENOENT));
   ASSERT_THAT(LIBC_NAMESPACE::statvfs("/nonexistent", buf), Fails(ENOENT));
@@ -38,7 +39,7 @@ TEST(LlvmLibcSysStatfsTest, InvalidPath) {
   ASSERT_THAT(LIBC_NAMESPACE::statvfs(nullptr, buf), Fails(EFAULT));
 }
 
-TEST(LlvmLibcSysStatfsTest, NameTooLong) {
+TEST(LlvmLibcSysStatfsTest, StatvfsNameTooLong) {
   statvfs buf[1];
   ASSERT_THAT(LIBC_NAMESPACE::statvfs("/", buf), Succeeds());
   char *name = static_cast<char *>(__builtin_alloca(buf->f_namemax + 3));

>From 8362fa5f21707f70c99212865578683cde9620de Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Thu, 21 Mar 2024 14:49:30 -0400
Subject: [PATCH 4/8] fix tests

---
 libc/src/sys/statvfs/linux/CMakeLists.txt         | 2 +-
 libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp | 4 ----
 libc/test/src/sys/statvfs/linux/statvfs_test.cpp  | 9 ++-------
 3 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/libc/src/sys/statvfs/linux/CMakeLists.txt b/libc/src/sys/statvfs/linux/CMakeLists.txt
index c481b07005728a..2d8554ae6234f6 100644
--- a/libc/src/sys/statvfs/linux/CMakeLists.txt
+++ b/libc/src/sys/statvfs/linux/CMakeLists.txt
@@ -7,7 +7,7 @@ add_header_library(
     libc.src.__support.OSUtil.osutil
     libc.src.__support.common
     libc.src.__support.CPP.optional
-    libc.include.sys.syscall
+    libc.include.sys_syscall
 )
 
 add_entrypoint_object(
diff --git a/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp b/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
index 5899e64fb91f10..d4c02c89c54bcb 100644
--- a/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
+++ b/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
@@ -37,10 +37,6 @@ TEST(LlvmLibcSysStatvfsTest, FstatfsBasic) {
   ASSERT_EQ(buf->f_type, static_cast<decltype(buf->f_type)>(SYSFS_MAGIC));
 }
 
-TEST(LlvmLibcSysStatvfsTest, FstatvfsNullBuffer) {
-  ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(PathFD("/"), nullptr), Fails(EFAULT));
-}
-
 TEST(LlvmLibcSysStatvfsTest, FstatvfsInvalidFD) {
   statvfs buf[1];
   ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(-1, buf), Fails(EBADF));
diff --git a/libc/test/src/sys/statvfs/linux/statvfs_test.cpp b/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
index 951edfeb22b20c..00a739bb4a9122 100644
--- a/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
+++ b/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
@@ -20,14 +20,9 @@ TEST(LlvmLibcSysStatfsTest, StatfsBasic) {
   statfs buf[1];
   ASSERT_THAT(LIBC_NAMESPACE::statfs("/", buf), Succeeds());
   ASSERT_THAT(LIBC_NAMESPACE::statfs("/proc", buf), Succeeds());
-  ASSERT_EQ(buf->f_type, static_cast<__kernel_long_t>(PROC_SUPER_MAGIC));
+  ASSERT_EQ(buf->f_type, static_cast<decltype(buf->f_type)>(PROC_SUPER_MAGIC));
   ASSERT_THAT(LIBC_NAMESPACE::statfs("/sys", buf), Succeeds());
-  ASSERT_EQ(buf->f_type, static_cast<__kernel_long_t>(SYSFS_MAGIC));
-}
-
-// POSIX API does not specify what happens when buf is NULL
-TEST(LlvmLibcSysStatfsTest, StatfsNullBuffer) {
-  ASSERT_THAT(LIBC_NAMESPACE::statfs("/", nullptr), Fails(EFAULT));
+  ASSERT_EQ(buf->f_type, static_cast<decltype(buf->f_type)>(SYSFS_MAGIC));
 }
 
 TEST(LlvmLibcSysStatfsTest, StatvfsInvalidPath) {

>From 87a10275216d3af4a73e5d340c3c90ca39da3213 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 22 Mar 2024 09:46:55 -0400
Subject: [PATCH 5/8] mark uninitialized

---
 libc/src/sys/statvfs/linux/statfs_utils.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libc/src/sys/statvfs/linux/statfs_utils.h b/libc/src/sys/statvfs/linux/statfs_utils.h
index 67e026cf8103d9..09b637f301acf1 100644
--- a/libc/src/sys/statvfs/linux/statfs_utils.h
+++ b/libc/src/sys/statvfs/linux/statfs_utils.h
@@ -26,6 +26,11 @@ using LinuxStatFs = statfs;
 #endif
 
 LIBC_INLINE cpp::optional<LinuxStatFs> linux_statfs(const char *path) {
+#ifdef __clang__
+  // Disable pattern filling for result buffer: this struct is to be populated
+  // by the syscall.
+  [[clang::uninitialized]]
+#endif
   LinuxStatFs result;
   // On 32-bit platforms, original statfs cannot handle large file systems.
   // In such cases, SYS_statfs64 is defined and should be used.

>From 51253fc4286881be1df64d18b8032b1c86cef03e Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Sun, 24 Mar 2024 15:05:20 -0400
Subject: [PATCH 6/8] add whitespaces to public headers

---
 libc/include/llvm-libc-types/struct_statvfs.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libc/include/llvm-libc-types/struct_statvfs.h b/libc/include/llvm-libc-types/struct_statvfs.h
index 265896e9b3a469..f467cfd936bdb6 100644
--- a/libc/include/llvm-libc-types/struct_statvfs.h
+++ b/libc/include/llvm-libc-types/struct_statvfs.h
@@ -8,8 +8,10 @@
 
 #ifndef LLVM_LIBC_TYPES_STRUCT_STATVFS_H
 #define LLVM_LIBC_TYPES_STRUCT_STATVFS_H
+
 #include <llvm-libc-types/fsblkcnt_t.h>
 #include <llvm-libc-types/fsfilcnt_t.h>
+
 struct statvfs {
   unsigned long f_bsize;   /* Filesystem block size */
   unsigned long f_frsize;  /* Fragment size */
@@ -23,4 +25,5 @@ struct statvfs {
   unsigned long f_flag;    /* Mount flags */
   unsigned long f_namemax; /* Maximum filename length */
 };
+
 #endif // LLVM_LIBC_TYPES_STRUCT_STATVFS_H

>From 61cb136fd31a8c2f4e9e6b4a6c914bcb952b7649 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Sun, 24 Mar 2024 15:11:28 -0400
Subject: [PATCH 7/8] comment about pattern filling

---
 libc/src/sys/statvfs/linux/statfs_utils.h | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/libc/src/sys/statvfs/linux/statfs_utils.h b/libc/src/sys/statvfs/linux/statfs_utils.h
index 09b637f301acf1..1a1bd6591f4a8d 100644
--- a/libc/src/sys/statvfs/linux/statfs_utils.h
+++ b/libc/src/sys/statvfs/linux/statfs_utils.h
@@ -26,11 +26,10 @@ using LinuxStatFs = statfs;
 #endif
 
 LIBC_INLINE cpp::optional<LinuxStatFs> linux_statfs(const char *path) {
-#ifdef __clang__
-  // Disable pattern filling for result buffer: this struct is to be populated
-  // by the syscall.
-  [[clang::uninitialized]]
-#endif
+  // 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.
@@ -47,6 +46,10 @@ LIBC_INLINE cpp::optional<LinuxStatFs> linux_statfs(const char *path) {
 }
 
 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.

>From 40d70882214d9898bffda632daef2868ca9720a4 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Sun, 24 Mar 2024 15:33:51 -0400
Subject: [PATCH 8/8] address CRs

---
 libc/src/sys/statvfs/linux/fstatvfs.cpp       |  9 ++++----
 libc/src/sys/statvfs/linux/statfs_utils.h     | 14 +++++++----
 libc/src/sys/statvfs/linux/statvfs.cpp        |  9 ++++----
 .../test/src/sys/statvfs/linux/CMakeLists.txt |  1 -
 .../src/sys/statvfs/linux/fstatvfs_test.cpp   | 23 +++++++++----------
 .../src/sys/statvfs/linux/statvfs_test.cpp    |  8 +++----
 6 files changed, 33 insertions(+), 31 deletions(-)

diff --git a/libc/src/sys/statvfs/linux/fstatvfs.cpp b/libc/src/sys/statvfs/linux/fstatvfs.cpp
index 03543f68f1efe3..e58f51cc851b75 100644
--- a/libc/src/sys/statvfs/linux/fstatvfs.cpp
+++ b/libc/src/sys/statvfs/linux/fstatvfs.cpp
@@ -14,11 +14,10 @@ namespace LIBC_NAMESPACE {
 
 LLVM_LIBC_FUNCTION(int, fstatvfs, (int fd, struct statvfs *buf)) {
   using namespace statfs_utils;
-  if (cpp::optional<LinuxStatFs> result = linux_fstatfs(fd)) {
-    *buf = statfs_to_statvfs(*result);
-    return 0;
-  }
-  return -1;
+  cpp::optional<LinuxStatFs> result = linux_fstatfs(fd);
+  if (result)
+    statfs_to_statvfs(*result, *buf);
+  return result ? 0 : -1;
 }
 
 } // namespace LIBC_NAMESPACE
diff --git a/libc/src/sys/statvfs/linux/statfs_utils.h b/libc/src/sys/statvfs/linux/statfs_utils.h
index 1a1bd6591f4a8d..41ce52cdd03a66 100644
--- a/libc/src/sys/statvfs/linux/statfs_utils.h
+++ b/libc/src/sys/statvfs/linux/statfs_utils.h
@@ -25,6 +25,9 @@ using LinuxStatFs = statfs64;
 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
@@ -42,6 +45,7 @@ LIBC_INLINE cpp::optional<LinuxStatFs> linux_statfs(const char *path) {
     libc_errno = -ret;
     return cpp::nullopt;
   }
+  result.f_flags &= ~ST_VALID;
   return result;
 }
 
@@ -62,12 +66,15 @@ LIBC_INLINE cpp::optional<LinuxStatFs> linux_fstatfs(int fd) {
     libc_errno = -ret;
     return cpp::nullopt;
   }
+  result.f_flags &= ~ST_VALID;
   return result;
 }
 
-// use struct stat(v)fs to avoid conflicts with the function names.
-LIBC_INLINE struct statvfs statfs_to_statvfs(const LinuxStatFs &in) {
-  struct statvfs out;
+// 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 void statfs_to_statvfs(const LinuxStatFs &__restrict in,
+                                   struct statvfs &__restrict out) {
   out.f_bsize = in.f_bsize;
   out.f_frsize = in.f_frsize;
   out.f_blocks = in.f_blocks;
@@ -80,7 +87,6 @@ LIBC_INLINE struct statvfs statfs_to_statvfs(const LinuxStatFs &in) {
                static_cast<decltype(out.f_fsid)>(in.f_fsid.val[1]) << 32;
   out.f_flag = in.f_flags;
   out.f_namemax = in.f_namelen;
-  return out;
 }
 } // namespace statfs_utils
 } // namespace LIBC_NAMESPACE
diff --git a/libc/src/sys/statvfs/linux/statvfs.cpp b/libc/src/sys/statvfs/linux/statvfs.cpp
index c71065930766f2..6bd8150b49d6a1 100644
--- a/libc/src/sys/statvfs/linux/statvfs.cpp
+++ b/libc/src/sys/statvfs/linux/statvfs.cpp
@@ -16,11 +16,10 @@ LLVM_LIBC_FUNCTION(int, statvfs,
                    (const char *__restrict path,
                     struct statvfs *__restrict buf)) {
   using namespace statfs_utils;
-  if (cpp::optional<LinuxStatFs> result = linux_statfs(path)) {
-    *buf = statfs_to_statvfs(*result);
-    return 0;
-  }
-  return -1;
+  cpp::optional<LinuxStatFs> result = linux_statfs(path);
+  if (result)
+    statfs_to_statvfs(*result, *buf);
+  return result ? 0 : -1;
 }
 
 } // namespace LIBC_NAMESPACE
diff --git a/libc/test/src/sys/statvfs/linux/CMakeLists.txt b/libc/test/src/sys/statvfs/linux/CMakeLists.txt
index 0a695bce968435..1f8688868e0438 100644
--- a/libc/test/src/sys/statvfs/linux/CMakeLists.txt
+++ b/libc/test/src/sys/statvfs/linux/CMakeLists.txt
@@ -25,6 +25,5 @@ add_libc_unittest(
     libc.src.sys.statvfs.fstatvfs
     libc.src.fcntl.open
     libc.src.unistd.close
-    libc.src.__support.CPP.string_view
     libc.test.UnitTest.ErrnoSetterMatcher
 )
diff --git a/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp b/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
index d4c02c89c54bcb..bd5195c7969bd8 100644
--- a/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
+++ b/libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
@@ -1,4 +1,4 @@
-#include "src/__support/CPP/string_view.h"
+#include "llvm-libc-macros/linux/fcntl-macros.h"
 #include "src/fcntl/open.h"
 #include "src/sys/statvfs/fstatvfs.h"
 #include "src/sys/statvfs/linux/statfs_utils.h"
@@ -6,7 +6,6 @@
 #include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/LibcTest.h"
 #include <linux/magic.h>
-#include <llvm-libc-macros/linux/fcntl-macros.h>
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
 
 namespace LIBC_NAMESPACE {
@@ -22,22 +21,22 @@ static int fstatfs(int fd, struct statfs *buf) {
 
 struct PathFD {
   int fd;
-  explicit PathFD(LIBC_NAMESPACE::cpp::string_view path)
-      : fd(LIBC_NAMESPACE::open(path.data(), O_CLOEXEC | O_PATH)) {}
+  explicit PathFD(const char *path)
+      : fd(LIBC_NAMESPACE::open(path, O_CLOEXEC | O_PATH)) {}
   ~PathFD() { LIBC_NAMESPACE::close(fd); }
   operator int() const { return fd; }
 };
 
 TEST(LlvmLibcSysStatvfsTest, FstatfsBasic) {
-  statfs buf[1];
-  ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/"), buf), Succeeds());
-  ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/proc"), buf), Succeeds());
-  ASSERT_EQ(buf->f_type, static_cast<decltype(buf->f_type)>(PROC_SUPER_MAGIC));
-  ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/sys"), buf), Succeeds());
-  ASSERT_EQ(buf->f_type, static_cast<decltype(buf->f_type)>(SYSFS_MAGIC));
+  struct statfs buf;
+  ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/"), &buf), Succeeds());
+  ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/proc"), &buf), Succeeds());
+  ASSERT_EQ(buf.f_type, static_cast<decltype(buf.f_type)>(PROC_SUPER_MAGIC));
+  ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/sys"), &buf), Succeeds());
+  ASSERT_EQ(buf.f_type, static_cast<decltype(buf.f_type)>(SYSFS_MAGIC));
 }
 
 TEST(LlvmLibcSysStatvfsTest, FstatvfsInvalidFD) {
-  statvfs buf[1];
-  ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(-1, buf), Fails(EBADF));
+  struct statvfs buf;
+  ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(-1, &buf), Fails(EBADF));
 }
diff --git a/libc/test/src/sys/statvfs/linux/statvfs_test.cpp b/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
index 00a739bb4a9122..10c573bf8ecdcc 100644
--- a/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
+++ b/libc/test/src/sys/statvfs/linux/statvfs_test.cpp
@@ -6,7 +6,7 @@
 using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
 
 namespace LIBC_NAMESPACE {
-static int statfs(const char *path, struct statfs *buf) {
+static int statfs(const char *path, statfs *buf) {
   using namespace statfs_utils;
   if (cpp::optional<LinuxStatFs> result = linux_statfs(path)) {
     *buf = *result;
@@ -17,7 +17,7 @@ static int statfs(const char *path, struct statfs *buf) {
 } // namespace LIBC_NAMESPACE
 
 TEST(LlvmLibcSysStatfsTest, StatfsBasic) {
-  statfs buf[1];
+  struct statfs buf[1];
   ASSERT_THAT(LIBC_NAMESPACE::statfs("/", buf), Succeeds());
   ASSERT_THAT(LIBC_NAMESPACE::statfs("/proc", buf), Succeeds());
   ASSERT_EQ(buf->f_type, static_cast<decltype(buf->f_type)>(PROC_SUPER_MAGIC));
@@ -26,7 +26,7 @@ TEST(LlvmLibcSysStatfsTest, StatfsBasic) {
 }
 
 TEST(LlvmLibcSysStatfsTest, StatvfsInvalidPath) {
-  statvfs buf[1];
+  struct statvfs buf[1];
   ASSERT_THAT(LIBC_NAMESPACE::statvfs("", buf), Fails(ENOENT));
   ASSERT_THAT(LIBC_NAMESPACE::statvfs("/nonexistent", buf), Fails(ENOENT));
   ASSERT_THAT(LIBC_NAMESPACE::statvfs("/dev/null/whatever", buf),
@@ -35,7 +35,7 @@ TEST(LlvmLibcSysStatfsTest, StatvfsInvalidPath) {
 }
 
 TEST(LlvmLibcSysStatfsTest, StatvfsNameTooLong) {
-  statvfs buf[1];
+  struct statvfs buf[1];
   ASSERT_THAT(LIBC_NAMESPACE::statvfs("/", buf), Succeeds());
   char *name = static_cast<char *>(__builtin_alloca(buf->f_namemax + 3));
   name[0] = '/';



More information about the libc-commits mailing list