28 changes: 28 additions & 0 deletions libc/src/sys/statvfs/linux/statvfs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===-- 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/__support/libc_assert.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;
cpp::optional<LinuxStatFs> result = linux_statfs(path);
if (result) {
LIBC_ASSERT(buf != nullptr);
*buf = statfs_to_statvfs(*result);
}
return result ? 0 : -1;
}

} // namespace LIBC_NAMESPACE
20 changes: 20 additions & 0 deletions libc/src/sys/statvfs/statvfs.h
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions libc/test/src/sys/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions libc/test/src/sys/statvfs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
add_subdirectory(${LIBC_TARGET_OS})
endif()
29 changes: 29 additions & 0 deletions libc/test/src/sys/statvfs/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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.test.UnitTest.ErrnoSetterMatcher
)
42 changes: 42 additions & 0 deletions libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#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"
#include "src/unistd/close.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 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(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) {
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) {
struct statvfs buf;
ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(-1, &buf), Fails(EBADF));
}
47 changes: 47 additions & 0 deletions libc/test/src/sys/statvfs/linux/statvfs_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#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) {
struct statfs buf;
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));
ASSERT_THAT(LIBC_NAMESPACE::statfs("/sys", &buf), Succeeds());
ASSERT_EQ(buf.f_type, static_cast<decltype(buf.f_type)>(SYSFS_MAGIC));
}

TEST(LlvmLibcSysStatfsTest, StatvfsInvalidPath) {
struct statvfs buf;
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, StatvfsNameTooLong) {
struct statvfs buf;
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));
}