diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 5bdbced5572249..cd2b52f18a9062 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -95,6 +95,7 @@ set(TARGET_LIBC_ENTRYPOINTS # unistd.h entrypoints libc.src.unistd.close libc.src.unistd.fsync + libc.src.unistd.lseek libc.src.unistd.read libc.src.unistd.rmdir libc.src.unistd.unlink diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td index fc208978b14a4c..b351ea6d5e2910 100644 --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -238,7 +238,7 @@ def ThreadsAPI : PublicAPI<"threads.h"> { } def UniStdAPI : PublicAPI<"unistd.h"> { - let Types = ["size_t", "ssize_t"]; + let Types = ["off_t", "size_t", "ssize_t"]; } def SysStatAPI : PublicAPI<"sys/stat.h"> { diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index e08c8fd42c21af..fc64dbf68b452d 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -99,6 +99,7 @@ set(TARGET_LIBC_ENTRYPOINTS # unistd.h entrypoints libc.src.unistd.close libc.src.unistd.fsync + libc.src.unistd.lseek libc.src.unistd.read libc.src.unistd.rmdir libc.src.unistd.unlink diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index 9370b5a37705e4..30e188878eacc0 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -124,7 +124,7 @@ add_gen_header( GEN_HDR stdio.h DEPENDS .llvm_libc_common_h - .llvm-libc-macros.stdio_macros + .llvm-libc-macros.file_seek_macros .llvm-libc-types.FILE .llvm-libc-types.size_t ) @@ -150,6 +150,7 @@ add_gen_header( GEN_HDR unistd.h DEPENDS .llvm_libc_common_h + .llvm-libc-macros.file_seek_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 index 9ef039e499bfe6..f583a915e69951 100644 --- a/libc/include/llvm-libc-macros/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/CMakeLists.txt @@ -9,7 +9,7 @@ add_header( ) add_header( - stdio_macros + file_seek_macros HDR - stdio-macros.h + file-seek-macros.h ) diff --git a/libc/include/llvm-libc-macros/stdio-macros.h b/libc/include/llvm-libc-macros/file-seek-macros.h similarity index 61% rename from libc/include/llvm-libc-macros/stdio-macros.h rename to libc/include/llvm-libc-macros/file-seek-macros.h index af75193c1ecfb9..04f397982f460d 100644 --- a/libc/include/llvm-libc-macros/stdio-macros.h +++ b/libc/include/llvm-libc-macros/file-seek-macros.h @@ -1,4 +1,4 @@ -//===-- Definition of macros from stdio.h ---------------------------------===// +//===-- Definition of macros to be used with file seek functions ----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,11 +6,11 @@ // //===----------------------------------------------------------------------===// -#ifndef __LLVM_LIBC_MACROS_STDIO_MACROS_H -#define __LLVM_LIBC_MACROS_STDIO_MACROS_H +#ifndef __LLVM_LIBC_MACROS_FILE_SEEK_MACROS_H +#define __LLVM_LIBC_MACROS_FILE_SEEK_MACROS_H #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 -#endif // __LLVM_LIBC_MACROS_STDIO_MACROS_H +#endif // __LLVM_LIBC_MACROS_FILE_SEEK_MACROS_H diff --git a/libc/include/stdio.h.def b/libc/include/stdio.h.def index d3b4cfaed7a957..a643e226649133 100644 --- a/libc/include/stdio.h.def +++ b/libc/include/stdio.h.def @@ -10,7 +10,7 @@ #define LLVM_LIBC_STDIO_H #include <__llvm-libc-common.h> -#include +#include %%public_api() diff --git a/libc/include/unistd.h.def b/libc/include/unistd.h.def index 42bab396b2d673..d7a2de93a03d04 100644 --- a/libc/include/unistd.h.def +++ b/libc/include/unistd.h.def @@ -10,6 +10,7 @@ #define LLVM_LIBC_UNISTD_H #include <__llvm-libc-common.h> +#include %%public_api() diff --git a/libc/spec/posix.td b/libc/spec/posix.td index d6e3d8564f79e4..b9641d8c362da8 100644 --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -222,6 +222,7 @@ def POSIX : StandardSpec<"POSIX"> { "unistd.h", [], // Macros [ + OffTType, SSizeTType, SizeTType, ], @@ -237,6 +238,11 @@ def POSIX : StandardSpec<"POSIX"> { RetValSpec, [ArgSpec] >, + FunctionSpec< + "lseek", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec] + >, FunctionSpec< "read", RetValSpec, diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt index 70210ebd5b7908..d67b0b33d179a8 100644 --- a/libc/src/unistd/CMakeLists.txt +++ b/libc/src/unistd/CMakeLists.txt @@ -16,6 +16,13 @@ add_entrypoint_object( .${LIBC_TARGET_OS}.fsync ) +add_entrypoint_object( + lseek + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.lseek +) + add_entrypoint_object( read ALIAS diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt index f990a820b8b983..29a2cd629a4bd0 100644 --- a/libc/src/unistd/linux/CMakeLists.txt +++ b/libc/src/unistd/linux/CMakeLists.txt @@ -24,6 +24,19 @@ add_entrypoint_object( libc.src.errno.errno ) +add_entrypoint_object( + lseek + SRCS + lseek.cpp + HDRS + ../lseek.h + DEPENDS + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + add_entrypoint_object( read SRCS diff --git a/libc/src/unistd/linux/lseek.cpp b/libc/src/unistd/linux/lseek.cpp new file mode 100644 index 00000000000000..559673f3f4370a --- /dev/null +++ b/libc/src/unistd/linux/lseek.cpp @@ -0,0 +1,39 @@ +//===-- Linux implementation of lseek -------------------------------------===// +// +// 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/lseek.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include // For syscall numbers. +#include + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(off_t, lseek, (int fd, off_t offset, int whence)) { + off_t result; +#ifdef SYS_lseek + long ret = __llvm_libc::syscall(SYS_lseek, fd, offset, whence); + result = ret; +#elif defined(SYS__llseek) + long ret = __llvm_libc::syscall(SYS__lseek, fd, offset >> 32, offset, &result, + whence); +#else +#error "lseek and _llseek syscalls not available." +#endif + + if (ret < 0) { + errno = -ret; + return -1; + } + return result; +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/lseek.h b/libc/src/unistd/lseek.h new file mode 100644 index 00000000000000..28252e4d491719 --- /dev/null +++ b/libc/src/unistd/lseek.h @@ -0,0 +1,20 @@ +//===-- Implementation header for lseek -------------------------*- 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_UNISTD_LSEEK_H +#define LLVM_LIBC_SRC_UNISTD_LSEEK_H + +#include + +namespace __llvm_libc { + +off_t lseek(int fd, off_t offset, int whence); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_LSEEK_H diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt index abaec098af3466..a60318a98fc9b1 100644 --- a/libc/test/src/unistd/CMakeLists.txt +++ b/libc/test/src/unistd/CMakeLists.txt @@ -19,6 +19,22 @@ add_libc_unittest( libc.test.errno_setter_matcher ) +add_libc_unittest( + lseek_test + SUITE + libc_unistd_unittests + SRCS + lseek_test.cpp + DEPENDS + libc.include.errno + libc.include.unistd + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.lseek + libc.src.unistd.read + libc.test.errno_setter_matcher +) + add_libc_unittest( rmdir_test SUITE diff --git a/libc/test/src/unistd/lseek_test.cpp b/libc/test/src/unistd/lseek_test.cpp new file mode 100644 index 00000000000000..1285c2d0653755 --- /dev/null +++ b/libc/test/src/unistd/lseek_test.cpp @@ -0,0 +1,61 @@ +//===-- Unittests for lseek -----------------------------------------------===// +// +// 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/close.h" +#include "src/unistd/lseek.h" +#include "src/unistd/read.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" +#include "utils/testutils/FDReader.h" + +#include +#include + +TEST(LlvmLibcUniStd, LseekTest) { + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char *TEST_FILE = "testdata/lseek.test"; + int fd = __llvm_libc::open(TEST_FILE, O_RDONLY); + ASSERT_EQ(errno, 0); + ASSERT_GT(fd, 0); + constexpr const char LSEEK_TEST[] = "lseek test"; + constexpr int LSEEK_TEST_SIZE = sizeof(LSEEK_TEST) - 1; + + char read_buf[20]; + ASSERT_THAT(__llvm_libc::read(fd, read_buf, LSEEK_TEST_SIZE), + Succeeds(LSEEK_TEST_SIZE)); + read_buf[LSEEK_TEST_SIZE] = '\0'; + EXPECT_STREQ(read_buf, LSEEK_TEST); + + // Seek to the beginning of the file and re-read. + ASSERT_THAT(__llvm_libc::lseek(fd, 0, SEEK_SET), Succeeds(0)); + ASSERT_THAT(__llvm_libc::read(fd, read_buf, LSEEK_TEST_SIZE), + Succeeds(LSEEK_TEST_SIZE)); + read_buf[LSEEK_TEST_SIZE] = '\0'; + EXPECT_STREQ(read_buf, LSEEK_TEST); + + // Seek to the beginning of the file from the end and re-read. + ASSERT_THAT(__llvm_libc::lseek(fd, -LSEEK_TEST_SIZE, SEEK_END), Succeeds(0)); + ASSERT_THAT(__llvm_libc::read(fd, read_buf, LSEEK_TEST_SIZE), + Succeeds(LSEEK_TEST_SIZE)); + read_buf[LSEEK_TEST_SIZE] = '\0'; + EXPECT_STREQ(read_buf, LSEEK_TEST); + + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); +} + +TEST(LlvmLibcUniStd, LseekFailsTest) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + constexpr const char *TEST_FILE = "testdata/lseek.test"; + int fd = __llvm_libc::open(TEST_FILE, O_RDONLY); + ASSERT_EQ(errno, 0); + ASSERT_GT(fd, 0); + EXPECT_THAT(__llvm_libc::lseek(fd, -1, SEEK_CUR), Fails(EINVAL)); + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); +} diff --git a/libc/test/src/unistd/testdata/CMakeLists.txt b/libc/test/src/unistd/testdata/CMakeLists.txt index 25d6247c1d975e..f302be251143cd 100644 --- a/libc/test/src/unistd/testdata/CMakeLists.txt +++ b/libc/test/src/unistd/testdata/CMakeLists.txt @@ -1,2 +1,4 @@ # This directory will be used to create test files and delete them # from tests. + +file(GENERATE OUTPUT lseek.test CONTENT "lseek test")