diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -21,7 +21,9 @@ libc.src.errno.__errno_location # fcntl.h entrypoints + libc.src.fcntl.creat libc.src.fcntl.open + libc.src.fcntl.openat # string.h entrypoints libc.src.string.bcmp diff --git a/libc/include/llvm-libc-macros/linux/fcntl-macros.h b/libc/include/llvm-libc-macros/linux/fcntl-macros.h --- a/libc/include/llvm-libc-macros/linux/fcntl-macros.h +++ b/libc/include/llvm-libc-macros/linux/fcntl-macros.h @@ -49,4 +49,8 @@ #define S_ISUID 04000 #define S_ISGID 02000 +// Sepcial directory FD to indicate that tha path argument to +// openat is relative to the current directory. +#define AT_FDCWD -100 + #endif // __LLVM_LIBC_MACROS_LINUX_FCNTL_MACROS_H diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -114,11 +114,21 @@ [ModeTType], [], // Enumerations [ + FunctionSpec< + "creat", + RetValSpec, + [ArgSpec, ArgSpec] + >, FunctionSpec< "open", RetValSpec, [ArgSpec, ArgSpec, ArgSpec] >, + FunctionSpec< + "openat", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec, ArgSpec] + >, ] >; diff --git a/libc/src/fcntl/CMakeLists.txt b/libc/src/fcntl/CMakeLists.txt --- a/libc/src/fcntl/CMakeLists.txt +++ b/libc/src/fcntl/CMakeLists.txt @@ -2,9 +2,23 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) endif() +add_entrypoint_object( + creat + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.creat +) + add_entrypoint_object( open ALIAS DEPENDS .${LIBC_TARGET_OS}.open ) + +add_entrypoint_object( + openat + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.openat +) diff --git a/libc/src/fcntl/creat.h b/libc/src/fcntl/creat.h new file mode 100644 --- /dev/null +++ b/libc/src/fcntl/creat.h @@ -0,0 +1,20 @@ +//===-- Implementation header of creat --------------------------*- 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_FCNTL_CREAT_H +#define LLVM_LIBC_SRC_FCNTL_CREAT_H + +#include + +namespace __llvm_libc { + +int creat(const char *path, int mode); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_FCNTL_CREAT_H diff --git a/libc/src/fcntl/linux/CMakeLists.txt b/libc/src/fcntl/linux/CMakeLists.txt --- a/libc/src/fcntl/linux/CMakeLists.txt +++ b/libc/src/fcntl/linux/CMakeLists.txt @@ -1,3 +1,16 @@ +add_entrypoint_object( + creat + SRCS + creat.cpp + HDRS + ../creat.h + DEPENDS + libc.include.errno + libc.include.fcntl + libc.src.__support.OSUtil.osutil + libc.src.errno.__errno_location +) + add_entrypoint_object( open SRCS @@ -10,3 +23,16 @@ libc.src.__support.OSUtil.osutil libc.src.errno.__errno_location ) + +add_entrypoint_object( + openat + SRCS + openat.cpp + HDRS + ../openat.h + DEPENDS + libc.include.errno + libc.include.fcntl + libc.src.__support.OSUtil.osutil + libc.src.errno.__errno_location +) diff --git a/libc/src/fcntl/linux/creat.cpp b/libc/src/fcntl/linux/creat.cpp new file mode 100644 --- /dev/null +++ b/libc/src/fcntl/linux/creat.cpp @@ -0,0 +1,30 @@ +//===-- Implementation of creat -------------------------------------------===// +// +// 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/creat.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, creat, (const char *path, int mode_flags)) { + int fd = __llvm_libc::syscall(SYS_open, path, O_CREAT | O_WRONLY | O_TRUNC, + mode_flags); + if (fd > 0) + return fd; + + errno = -fd; + return -1; +} + +} // namespace __llvm_libc diff --git a/libc/src/fcntl/linux/openat.cpp b/libc/src/fcntl/linux/openat.cpp new file mode 100644 --- /dev/null +++ b/libc/src/fcntl/linux/openat.cpp @@ -0,0 +1,40 @@ +//===-- Implementation of openat ------------------------------------------===// +// +// 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/openat.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(int, openat, (int dfd, const char *path, int flags, ...)) { + mode_t mode_flags = 0; + // O_TMPFILE is a multi-bit flag so we test all bits by checking for equality + // with O_TMPFILE + if ((flags & O_CREAT) || (flags & O_TMPFILE) == O_TMPFILE) { + va_list varargs; + va_start(varargs, flags); + mode_flags = va_arg(varargs, mode_t); + va_end(varargs); + } + + int fd = __llvm_libc::syscall(SYS_openat, dfd, path, flags, mode_flags); + if (fd > 0) + return fd; + + errno = -fd; + return -1; +} + +} // namespace __llvm_libc diff --git a/libc/src/fcntl/openat.h b/libc/src/fcntl/openat.h new file mode 100644 --- /dev/null +++ b/libc/src/fcntl/openat.h @@ -0,0 +1,20 @@ +//===-- Implementation header of openat -------------------------*- 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_FCNTL_OPENAT_H +#define LLVM_LIBC_SRC_FCNTL_OPENAT_H + +#include + +namespace __llvm_libc { + +int openat(int dfd, const char *path, int flags, ...); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_FCNTL_OPENAT_H diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -35,6 +35,7 @@ add_subdirectory(stdlib) if(${LIBC_TARGET_OS} STREQUAL "linux") + add_subdirectory(fcntl) add_subdirectory(sys) add_subdirectory(unistd) endif() diff --git a/libc/test/src/fcntl/CMakeLists.txt b/libc/test/src/fcntl/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/fcntl/CMakeLists.txt @@ -0,0 +1,34 @@ +add_libc_testsuite(libc_fcntl_unittests) + +add_subdirectory(testdata) + +add_libc_unittest( + creat_test + SUITE + libc_fcntl_unittests + SRCS + creat_test.cpp + DEPENDS + libc.include.errno + libc.include.fcntl + libc.src.fcntl.creat + libc.src.fcntl.open + libc.src.unistd.close + libc.test.errno_setter_matcher +) + +add_libc_unittest( + openat_test + SUITE + libc_fcntl_unittests + SRCS + openat_test.cpp + DEPENDS + libc.include.errno + libc.include.fcntl + libc.src.fcntl.open + libc.src.fcntl.openat + libc.src.unistd.close + libc.src.unistd.read + libc.test.errno_setter_matcher +) diff --git a/libc/test/src/fcntl/creat_test.cpp b/libc/test/src/fcntl/creat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/fcntl/creat_test.cpp @@ -0,0 +1,32 @@ +//===-- Unittest for creat ------------------------------------------------===// +// +// 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/creat.h" +#include "src/fcntl/open.h" +#include "src/unistd/close.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" +#include "utils/testutils/FDReader.h" + +#include + +TEST(LlvmLibcCreatTest, CreatAndOpen) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char *TEST_FILE = "testdata/creat.test"; + int fd = __llvm_libc::creat(TEST_FILE, S_IRWXU); + ASSERT_EQ(errno, 0); + ASSERT_GT(fd, 0); + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); + + fd = __llvm_libc::open(TEST_FILE, O_RDONLY); + ASSERT_EQ(errno, 0); + ASSERT_GT(fd, 0); + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); + + // TODO: 'remove' the test file at the end. +} diff --git a/libc/test/src/fcntl/openat_test.cpp b/libc/test/src/fcntl/openat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/fcntl/openat_test.cpp @@ -0,0 +1,43 @@ +//===-- Unittests for openat ----------------------------------------------===// +// +// 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/fcntl/openat.h" +#include "src/unistd/close.h" +#include "src/unistd/read.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" +#include "utils/testutils/FDReader.h" + +#include + +TEST(LlvmLibcUniStd, OpenAndReadTest) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char *TEST_DIR = "testdata"; + constexpr const char *TEST_FILE = "openat.test"; + int dir_fd = __llvm_libc::open(TEST_DIR, O_DIRECTORY); + ASSERT_EQ(errno, 0); + ASSERT_GT(dir_fd, 0); + constexpr const char TEST_MSG[] = "openat test"; + constexpr int TEST_MSG_SIZE = sizeof(TEST_MSG) - 1; + + int read_fd = __llvm_libc::openat(dir_fd, TEST_FILE, O_RDONLY); + ASSERT_EQ(errno, 0); + ASSERT_GT(read_fd, 0); + char read_buf[TEST_MSG_SIZE]; + ASSERT_THAT(__llvm_libc::read(read_fd, read_buf, TEST_MSG_SIZE), + Succeeds(TEST_MSG_SIZE)); + ASSERT_THAT(__llvm_libc::close(read_fd), Succeeds(0)); + ASSERT_THAT(__llvm_libc::close(dir_fd), Succeeds(0)); +} + +TEST(LlvmLibcUniStd, FailTest) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + EXPECT_THAT(__llvm_libc::openat(AT_FDCWD, "openat.test", O_RDONLY), + Fails(ENOENT)); +} diff --git a/libc/test/src/fcntl/testdata/CMakeLists.txt b/libc/test/src/fcntl/testdata/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/fcntl/testdata/CMakeLists.txt @@ -0,0 +1 @@ +file(GENERATE OUTPUT openat.test CONTENT "openat test")