47 changes: 47 additions & 0 deletions libc/src/sys/stat/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,50 @@ add_entrypoint_object(
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)

add_header_library(
kernel_statx
HDRS
kernel_statx.h
DEPENDS
libc.include.sys_stat
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)

add_entrypoint_object(
stat
SRCS
stat.cpp
HDRS
../stat.h
DEPENDS
.kernel_statx
libc.include.fcntl
libc.include.sys_stat
)

add_entrypoint_object(
lstat
SRCS
lstat.cpp
HDRS
../lstat.h
DEPENDS
.kernel_statx
libc.include.fcntl
libc.include.sys_stat
)

add_entrypoint_object(
fstat
SRCS
fstat.cpp
HDRS
../fstat.h
DEPENDS
.kernel_statx
libc.include.fcntl
libc.include.sys_stat
)
23 changes: 23 additions & 0 deletions libc/src/sys/stat/linux/fstat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- Linux implementation of fstat -------------------------------------===//
//
// 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/stat/fstat.h"
#include "kernel_statx.h"

#include "src/__support/common.h"

#include <fcntl.h>
#include <sys/stat.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, fstat, (int fd, struct stat *statbuf)) {
return statx(fd, "", AT_EMPTY_PATH, statbuf);
}

} // namespace __llvm_libc
105 changes: 105 additions & 0 deletions libc/src/sys/stat/linux/kernel_statx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//===-- Wrapper over SYS_statx syscall ------------------------------------===//
//
// 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_STAT_LINUX_STATX_H
#define LLVM_LIBC_SRC_SYS_STAT_LINUX_STATX_H

#include "src/__support/OSUtil/syscall.h" // For internal syscall function.

#include <errno.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/syscall.h> // For syscall numbers.

// It is safe to include this kernel header as it is designed to be
// included from user programs without causing any name pollution.
#include <linux/kdev_t.h>

namespace {

// The type definitions in the internal namespace match kernel's definition of
// the statx_timestamp and statx types in linux/stat.h. We define equivalent
// types here instead of including that header file to avoid name mixup between
// linux/stat.h and the libc's stat.h.
struct statx_timestamp {
int64_t tv_sec;
uint32_t tv_nsec;
int32_t __reserved;
};

struct statx_buf {
uint32_t stx_mask; // What results were written
uint32_t stx_blksize; // Preferred general I/O size
uint64_t stx_attributes; // Flags conveying information about the file
uint32_t stx_nlink; // Number of hard links
uint32_t stx_uid; // User ID of owner
uint32_t stx_gid; // Group ID of owner
uint16_t stx_mode; // File mode
uint16_t __spare0[1];
uint64_t stx_ino; // Inode number
uint64_t stx_size; // File size
uint64_t stx_blocks; // Number of 512-byte blocks allocated
uint64_t stx_attributes_mask; // Mask to show what's supported in
// stx_attributes
struct statx_timestamp stx_atime; // Last access time
struct statx_timestamp stx_btime; // File creation time
struct statx_timestamp stx_ctime; // Last attribute change time
struct statx_timestamp stx_mtime; // Last data modification time
uint32_t stx_rdev_major; // Device ID of special file
uint32_t stx_rdev_minor;
uint32_t stx_dev_major; // ID of device containing file
uint32_t stx_dev_minor;
uint64_t stx_mnt_id;
uint64_t __spare2;
uint64_t __spare3[12]; // Spare space for future expansion
};

// The below mask value is based on the definition of a similarly
// named macro in linux/stat.h. When this flag is passed for the
// mask argument to the statx syscall, all fields except the
// stx_btime field will be filled in.
constexpr unsigned int STATX_BASIC_STATS_MASK = 0x7FF;

} // Anonymous namespace

namespace __llvm_libc {

inline int statx(int dirfd, const char *__restrict path, int flags,
struct stat *__restrict statbuf) {
// We make a statx syscall and copy out the result into the |statbuf|.
::statx_buf xbuf;
long ret =
syscall(SYS_statx, dirfd, path, flags, ::STATX_BASIC_STATS_MASK, &xbuf);
if (ret < 0) {
errno = -ret;
return -1;
}

statbuf->st_dev = MKDEV(xbuf.stx_dev_major, xbuf.stx_dev_minor);
statbuf->st_ino = xbuf.stx_ino;
statbuf->st_mode = xbuf.stx_mode;
statbuf->st_nlink = xbuf.stx_nlink;
statbuf->st_uid = xbuf.stx_uid;
statbuf->st_gid = xbuf.stx_gid;
statbuf->st_rdev = MKDEV(xbuf.stx_rdev_major, xbuf.stx_rdev_minor);
statbuf->st_size = xbuf.stx_size;
statbuf->st_atim.tv_sec = xbuf.stx_atime.tv_sec;
statbuf->st_atim.tv_nsec = xbuf.stx_atime.tv_nsec;
statbuf->st_mtim.tv_sec = xbuf.stx_mtime.tv_sec;
statbuf->st_mtim.tv_nsec = xbuf.stx_mtime.tv_nsec;
statbuf->st_ctim.tv_sec = xbuf.stx_ctime.tv_sec;
statbuf->st_ctim.tv_nsec = xbuf.stx_ctime.tv_nsec;
statbuf->st_blksize = xbuf.stx_blksize;
statbuf->st_blocks = xbuf.stx_blocks;

return 0;
}

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_SYS_STAT_LINUX_STATX_H
26 changes: 26 additions & 0 deletions libc/src/sys/stat/linux/lstat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===-- Linux implementation of lstat -------------------------------------===//
//
// 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/stat/lstat.h"
#include "kernel_statx.h"

#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/common.h"

#include <fcntl.h>
#include <sys/stat.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, lstat,
(const char *__restrict path,
struct stat *__restrict statbuf)) {
return statx(AT_FDCWD, path, AT_SYMLINK_NOFOLLOW, statbuf);
}

} // namespace __llvm_libc
25 changes: 25 additions & 0 deletions libc/src/sys/stat/linux/stat.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===-- Linux implementation of stat --------------------------------------===//
//
// 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/stat/stat.h"
#include "kernel_statx.h"

#include "src/__support/common.h"

#include <fcntl.h>
#include <sys/stat.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, stat,
(const char *__restrict path,
struct stat *__restrict statbuf)) {
return statx(AT_FDCWD, path, 0, statbuf);
}

} // namespace __llvm_libc
20 changes: 20 additions & 0 deletions libc/src/sys/stat/lstat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for lstat -------------------------*- 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_STAT_LSTAT_H
#define LLVM_LIBC_SRC_SYS_STAT_LSTAT_H

#include <sys/stat.h>

namespace __llvm_libc {

int lstat(const char *__restrict path, struct stat *__restrict statbuf);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_SYS_STAT_LSTAT_H
20 changes: 20 additions & 0 deletions libc/src/sys/stat/stat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for stat --------------------------*- 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_STAT_STAT_H
#define LLVM_LIBC_SRC_SYS_STAT_STAT_H

#include <sys/stat.h>

namespace __llvm_libc {

int stat(const char *__restrict path, struct stat *__restrict statbuf);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_SYS_STAT_STAT_H
48 changes: 48 additions & 0 deletions libc/test/src/sys/stat/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,51 @@ add_libc_unittest(
libc.src.sys.stat.mkdirat
libc.src.unistd.rmdir
)

add_libc_unittest(
stat_test
SUITE
libc_sys_stat_unittests
SRCS
stat_test.cpp
DEPENDS
libc.include.errno
libc.include.fcntl
libc.include.sys_stat
libc.src.sys.stat.stat
libc.src.fcntl.open
libc.src.unistd.close
libc.src.unistd.unlink
)

add_libc_unittest(
lstat_test
SUITE
libc_sys_stat_unittests
SRCS
lstat_test.cpp
DEPENDS
libc.include.errno
libc.include.fcntl
libc.include.sys_stat
libc.src.sys.stat.lstat
libc.src.fcntl.open
libc.src.unistd.close
libc.src.unistd.unlink
)

add_libc_unittest(
fstat_test
SUITE
libc_sys_stat_unittests
SRCS
fstat_test.cpp
DEPENDS
libc.include.errno
libc.include.fcntl
libc.include.sys_stat
libc.src.sys.stat.fstat
libc.src.fcntl.open
libc.src.unistd.close
libc.src.unistd.unlink
)
51 changes: 51 additions & 0 deletions libc/test/src/sys/stat/fstat_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===-- Unittests for fstat -----------------------------------------------===//
//
// 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/fcntl/open.h"
#include "src/sys/stat/fstat.h"
#include "src/unistd/close.h"
#include "src/unistd/unlink.h"
#include "test/ErrnoSetterMatcher.h"
#include "utils/UnitTest/Test.h"
#include "utils/testutils/FDReader.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

TEST(LlvmLibcFStatTest, CreatAndReadMode) {
using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;

// The test file is initially writable. We open it for writing and ensure
// that it indeed can be opened for writing. Next, we close the file and
// make it readonly using chmod. We test that chmod actually succeeded by
// trying to open the file for writing and failing.
constexpr const char *TEST_FILE = "testdata/fstat.test";
errno = 0;

int fd = __llvm_libc::open(TEST_FILE, O_CREAT | O_WRONLY, S_IRWXU);
ASSERT_GT(fd, 0);
ASSERT_EQ(errno, 0);

struct stat statbuf;
ASSERT_THAT(__llvm_libc::fstat(fd, &statbuf), Succeeds(0));

ASSERT_EQ(int(statbuf.st_mode), int(S_IRWXU | S_IFREG));

ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0));
ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0));
}

TEST(LlvmLibcFStatTest, NonExistentFile) {
errno = 0;
using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
struct stat statbuf;
ASSERT_THAT(__llvm_libc::fstat(-1, &statbuf), Fails(EBADF));
errno = 0;
}
51 changes: 51 additions & 0 deletions libc/test/src/sys/stat/lstat_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===-- Unittests for lstat -----------------------------------------------===//
//
// 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/fcntl/open.h"
#include "src/sys/stat/lstat.h"
#include "src/unistd/close.h"
#include "src/unistd/unlink.h"
#include "test/ErrnoSetterMatcher.h"
#include "utils/UnitTest/Test.h"
#include "utils/testutils/FDReader.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

TEST(LlvmLibcLStatTest, CreatAndReadMode) {
using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;

// The test file is initially writable. We open it for writing and ensure
// that it indeed can be opened for writing. Next, we close the file and
// make it readonly using chmod. We test that chmod actually succeeded by
// trying to open the file for writing and failing.
constexpr const char *TEST_FILE = "testdata/lstat.test";
errno = 0;

int fd = __llvm_libc::open(TEST_FILE, O_CREAT | O_WRONLY, S_IRWXU);
ASSERT_GT(fd, 0);
ASSERT_EQ(errno, 0);
ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0));

struct stat statbuf;
ASSERT_THAT(__llvm_libc::lstat(TEST_FILE, &statbuf), Succeeds(0));

ASSERT_EQ(int(statbuf.st_mode), int(S_IRWXU | S_IFREG));

ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0));
}

TEST(LlvmLibcLStatTest, NonExistentFile) {
errno = 0;
using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
struct stat statbuf;
ASSERT_THAT(__llvm_libc::lstat("non-existent-file", &statbuf), Fails(ENOENT));
errno = 0;
}
51 changes: 51 additions & 0 deletions libc/test/src/sys/stat/stat_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===-- Unittests for stat ------------------------------------------------===//
//
// 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/fcntl/open.h"
#include "src/sys/stat/stat.h"
#include "src/unistd/close.h"
#include "src/unistd/unlink.h"
#include "test/ErrnoSetterMatcher.h"
#include "utils/UnitTest/Test.h"
#include "utils/testutils/FDReader.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>

TEST(LlvmLibcStatTest, CreatAndReadMode) {
using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;

// The test file is initially writable. We open it for writing and ensure
// that it indeed can be opened for writing. Next, we close the file and
// make it readonly using chmod. We test that chmod actually succeeded by
// trying to open the file for writing and failing.
constexpr const char *TEST_FILE = "testdata/stat.test";
errno = 0;

int fd = __llvm_libc::open(TEST_FILE, O_CREAT | O_WRONLY, S_IRWXU);
ASSERT_GT(fd, 0);
ASSERT_EQ(errno, 0);
ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0));

struct stat statbuf;
ASSERT_THAT(__llvm_libc::stat(TEST_FILE, &statbuf), Succeeds(0));

ASSERT_EQ(int(statbuf.st_mode), int(S_IRWXU | S_IFREG));

ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0));
}

TEST(LlvmLibcStatTest, NonExistentFile) {
errno = 0;
using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
struct stat statbuf;
ASSERT_THAT(__llvm_libc::stat("non-existent-file", &statbuf), Fails(ENOENT));
errno = 0;
}