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 @@ -111,6 +111,7 @@ libc.src.sys.stat.mkdirat # unistd.h entrypoints + libc.src.unistd.access libc.src.unistd.chdir libc.src.unistd.close libc.src.unistd.dup diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -168,6 +168,7 @@ DEPENDS .llvm_libc_common_h .llvm-libc-macros.file_seek_macros + .llvm-libc-macros.unistd_macros .llvm-libc-types.size_t .llvm-libc-types.ssize_t ) diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt --- a/libc/include/llvm-libc-macros/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/CMakeLists.txt @@ -21,3 +21,11 @@ DEPENDS .linux.sys_stat_macros ) + +add_header( + unistd_macros + HDR + unistd-macros.h + DEPENDS + .linux.unistd_macros +) diff --git a/libc/include/llvm-libc-macros/linux/CMakeLists.txt b/libc/include/llvm-libc-macros/linux/CMakeLists.txt --- a/libc/include/llvm-libc-macros/linux/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/linux/CMakeLists.txt @@ -9,3 +9,9 @@ HDR sys-stat-macros.h ) + +add_header( + unistd_macros + HDR + unistd-macros.h +) diff --git a/libc/include/llvm-libc-macros/linux/unistd-macros.h b/libc/include/llvm-libc-macros/linux/unistd-macros.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-macros/linux/unistd-macros.h @@ -0,0 +1,18 @@ +//===-- Definition of macros from unistd.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_MACROS_LINUX_UNISTD_MACROS_H +#define __LLVM_LIBC_MACROS_LINUX_UNISTD_MACROS_H + +// Values for mode argument to the access(...) function. +#define F_OK 0 +#define X_OK 1 +#define W_OK 2 +#define R_OK 4 + +#endif // __LLVM_LIBC_MACROS_LINUX_UNISTD_MACROS_H diff --git a/libc/include/llvm-libc-macros/unistd-macros.h b/libc/include/llvm-libc-macros/unistd-macros.h new file mode 100644 --- /dev/null +++ b/libc/include/llvm-libc-macros/unistd-macros.h @@ -0,0 +1,8 @@ +#ifndef __LLVM_LIBC_MACROS_UNISTD_MACROS_H +#define __LLVM_LIBC_MACROS_UNISTD_MACROS_H + +#ifdef __unix__ +#include "linux/unistd-macros.h" +#endif + +#endif // __LLVM_LIBC_MACROS_UNISTD_MACROS_H diff --git a/libc/include/unistd.h.def b/libc/include/unistd.h.def --- a/libc/include/unistd.h.def +++ b/libc/include/unistd.h.def @@ -11,6 +11,7 @@ #include <__llvm-libc-common.h> #include +#include %%public_api() diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -261,6 +261,11 @@ ], [], // Enumerations [ + FunctionSpec< + "access", + RetValSpec, + [ArgSpec, ArgSpec] + >, FunctionSpec< "chdir", RetValSpec, diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt --- a/libc/src/unistd/CMakeLists.txt +++ b/libc/src/unistd/CMakeLists.txt @@ -2,6 +2,13 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) endif() +add_entrypoint_object( + access + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.access +) + add_entrypoint_object( chdir ALIAS diff --git a/libc/include/unistd.h.def b/libc/src/unistd/access.h copy from libc/include/unistd.h.def copy to libc/src/unistd/access.h --- a/libc/include/unistd.h.def +++ b/libc/src/unistd/access.h @@ -1,4 +1,4 @@ -//===-- C standard library header unistd.h --------------------------------===// +//===-- Implementation header for access ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,12 +6,13 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_LIBC_UNISTD_H -#define LLVM_LIBC_UNISTD_H +#ifndef LLVM_LIBC_SRC_UNISTD_ACCESS_H +#define LLVM_LIBC_SRC_UNISTD_ACCESS_H -#include <__llvm-libc-common.h> -#include +namespace __llvm_libc { -%%public_api() +int access(const char *path, int mode); -#endif // LLVM_LIBC_UNISTD_H +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_ACCESS_H diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt --- a/libc/src/unistd/linux/CMakeLists.txt +++ b/libc/src/unistd/linux/CMakeLists.txt @@ -1,3 +1,16 @@ +add_entrypoint_object( + access + SRCS + access.cpp + HDRS + ../access.h + DEPENDS + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + add_entrypoint_object( chdir SRCS diff --git a/libc/src/unistd/linux/access.cpp b/libc/src/unistd/linux/access.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/access.cpp @@ -0,0 +1,36 @@ +//===-- Linux implementation of access ------------------------------------===// +// +// 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/unistd/access.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, access, (const char *path, int mode)) { +#ifdef SYS_access + long ret = __llvm_libc::syscall(SYS_access, path, mode); +#elif defined(SYS_accessat) + long ret = __llvm_libc::syscall(SYS_accessat, AT_FDCWD, path, mode); +#else +#error "access syscalls not available." +#endif + + if (ret < 0) { + errno = -ret; + return -1; + } + return 0; +} + +} // namespace __llvm_libc diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt --- a/libc/test/src/unistd/CMakeLists.txt +++ b/libc/test/src/unistd/CMakeLists.txt @@ -2,6 +2,21 @@ add_subdirectory(testdata) +add_libc_unittest( + access_test + SUITE + libc_unistd_unittests + SRCS + access_test.cpp + DEPENDS + libc.include.errno + libc.include.unistd + libc.src.fcntl.open + libc.src.unistd.access + libc.src.unistd.close + libc.src.unistd.unlink +) + add_libc_unittest( chdir_test SUITE diff --git a/libc/test/src/unistd/access_test.cpp b/libc/test/src/unistd/access_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/unistd/access_test.cpp @@ -0,0 +1,58 @@ +//===-- Unittests for access ----------------------------------------------===// +// +// 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/unistd/access.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 +#include + +TEST(LlvmLibcAccessTest, CreateAndTest) { + // The test strategy is to repeated create a file in different modes and test + // that it is accessable in those modes but not in others. + errno = 0; + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char *TEST_FILE = "testdata/access.test"; + int fd = __llvm_libc::open(TEST_FILE, O_WRONLY | O_CREAT, S_IRWXU); + ASSERT_EQ(errno, 0); + ASSERT_GT(fd, 0); + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); + + ASSERT_EQ(__llvm_libc::access(TEST_FILE, F_OK), 0); + ASSERT_EQ(errno, 0); + ASSERT_EQ(__llvm_libc::access(TEST_FILE, X_OK | W_OK | R_OK), 0); + ASSERT_EQ(errno, 0); + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0)); + + fd = __llvm_libc::open(TEST_FILE, O_WRONLY | O_CREAT, S_IXUSR); + ASSERT_EQ(errno, 0); + ASSERT_GT(fd, 0); + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); + ASSERT_EQ(__llvm_libc::access(TEST_FILE, F_OK), 0); + ASSERT_EQ(errno, 0); + ASSERT_EQ(__llvm_libc::access(TEST_FILE, X_OK), 0); + ASSERT_EQ(errno, 0); + ASSERT_EQ(__llvm_libc::access(TEST_FILE, R_OK), -1); + ASSERT_EQ(errno, EACCES); + errno = 0; + ASSERT_EQ(__llvm_libc::access(TEST_FILE, W_OK), -1); + ASSERT_EQ(errno, EACCES); + errno = 0; + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0)); +} + +TEST(LlvmLibcAccessTest, AccessNonExistentFile) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + ASSERT_THAT(__llvm_libc::access("testdata/non-existent-file", F_OK), + Fails(ENOENT)); +}