diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -95,6 +95,7 @@ # 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 --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -238,7 +238,7 @@ } 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 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -99,6 +99,7 @@ # 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 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -124,7 +124,7 @@ 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 @@ 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 --- a/libc/include/llvm-libc-macros/CMakeLists.txt +++ b/libc/include/llvm-libc-macros/CMakeLists.txt @@ -9,7 +9,7 @@ ) 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 rename from libc/include/llvm-libc-macros/stdio-macros.h rename to libc/include/llvm-libc-macros/file-seek-macros.h diff --git a/libc/include/stdio.h.def b/libc/include/stdio.h.def --- 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 --- 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 --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -222,6 +222,7 @@ "unistd.h", [], // Macros [ + OffTType, SSizeTType, SizeTType, ], @@ -237,6 +238,11 @@ 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 --- a/libc/src/unistd/CMakeLists.txt +++ b/libc/src/unistd/CMakeLists.txt @@ -16,6 +16,13 @@ .${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 --- a/libc/src/unistd/linux/CMakeLists.txt +++ b/libc/src/unistd/linux/CMakeLists.txt @@ -24,6 +24,19 @@ 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 --- /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 --- /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 --- a/libc/test/src/unistd/CMakeLists.txt +++ b/libc/test/src/unistd/CMakeLists.txt @@ -19,6 +19,22 @@ 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 --- /dev/null +++ b/libc/test/src/unistd/lseek_test.cpp @@ -0,0 +1,61 @@ +//===-- Unittests for read and write --------------------------------------===// +// +// 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 --- 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")