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 @@ -123,6 +123,8 @@ libc.src.unistd.link libc.src.unistd.linkat libc.src.unistd.lseek + libc.src.unistd.pread + libc.src.unistd.pwrite libc.src.unistd.read libc.src.unistd.readlink libc.src.unistd.readlinkat 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 @@ -123,6 +123,8 @@ libc.src.unistd.link libc.src.unistd.linkat libc.src.unistd.lseek + libc.src.unistd.pread + libc.src.unistd.pwrite libc.src.unistd.read libc.src.unistd.readlink libc.src.unistd.readlinkat diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -321,6 +321,16 @@ RetValSpec, [ArgSpec, ArgSpec, ArgSpec] >, + FunctionSpec< + "pread", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec, ArgSpec] + >, + FunctionSpec< + "pwrite", + RetValSpec, + [ArgSpec, 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 @@ -86,6 +86,20 @@ .${LIBC_TARGET_OS}.lseek ) +add_entrypoint_object( + pread + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.pread +) + +add_entrypoint_object( + pwrite + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.pwrite +) + 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 @@ -157,6 +157,32 @@ libc.src.errno.errno ) +add_entrypoint_object( + pread + SRCS + pread.cpp + HDRS + ../pread.h + DEPENDS + libc.include.unistd + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) + +add_entrypoint_object( + pwrite + SRCS + pwrite.cpp + HDRS + ../pwrite.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/pread.cpp b/libc/src/unistd/linux/pread.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/pread.cpp @@ -0,0 +1,29 @@ +//===-- Linux implementation of pread -------------------------------------===// +// +// 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/pread.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(ssize_t, pread, + (int fd, void *buf, size_t count, off_t offset)) { + long ret = __llvm_libc::syscall(SYS_pread64, fd, buf, count, offset); + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/linux/pwrite.cpp b/libc/src/unistd/linux/pwrite.cpp new file mode 100644 --- /dev/null +++ b/libc/src/unistd/linux/pwrite.cpp @@ -0,0 +1,29 @@ +//===-- Linux implementation of pwrite ------------------------------------===// +// +// 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/pwrite.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" + +#include +#include // For syscall numbers. + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(ssize_t, pwrite, + (int fd, const void *buf, size_t count, off_t offset)) { + long ret = __llvm_libc::syscall(SYS_pwrite64, fd, buf, count, offset); + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/unistd/pread.h b/libc/src/unistd/pread.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/pread.h @@ -0,0 +1,20 @@ +//===-- Implementation header for pread -------------------------*- 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_PREAD_H +#define LLVM_LIBC_SRC_UNISTD_PREAD_H + +#include + +namespace __llvm_libc { + +ssize_t pread(int fd, void *buf, size_t count, off_t offset); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_PREAD_H diff --git a/libc/src/unistd/pwrite.h b/libc/src/unistd/pwrite.h new file mode 100644 --- /dev/null +++ b/libc/src/unistd/pwrite.h @@ -0,0 +1,20 @@ +//===-- Implementation header for pwrite ------------------------*- 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_PWRITE_H +#define LLVM_LIBC_SRC_UNISTD_PWRITE_H + +#include + +namespace __llvm_libc { + +ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_UNISTD_PWRITE_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 @@ -119,6 +119,25 @@ libc.src.__support.CPP.string_view ) +add_libc_unittest( + pread_pwrite_test + SUITE + libc_unistd_unittests + SRCS + pread_pwrite_test.cpp + DEPENDS + libc.include.errno + libc.include.unistd + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.fsync + libc.src.unistd.pread + libc.src.unistd.pwrite + libc.src.unistd.unlink + libc.src.unistd.write + libc.test.errno_setter_matcher +) + add_libc_unittest( read_write_test SUITE diff --git a/libc/test/src/unistd/pread_pwrite_test.cpp b/libc/test/src/unistd/pread_pwrite_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/unistd/pread_pwrite_test.cpp @@ -0,0 +1,74 @@ +//===-- Unittests for pread and pwrite ------------------------------------===// +// +// 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/fsync.h" +#include "src/unistd/pread.h" +#include "src/unistd/pwrite.h" +#include "src/unistd/unlink.h" +#include "src/unistd/write.h" +#include "test/ErrnoSetterMatcher.h" +#include "utils/UnitTest/Test.h" +#include "utils/testutils/FDReader.h" + +#include + +TEST(LlvmLibcUniStd, PWriteAndPReadBackTest) { + // The strategy here is that we first create a file and write to it. Next, + // we open that file again and write at an offset. Finally, we open the + // file again and pread at an offset and make sure that only expected data + // is being read back. This also confirms that pwrite happened successfully. + constexpr const char HELLO[] = "hello"; + constexpr int HELLO_SIZE = sizeof(HELLO); + constexpr off_t OFFSET = 3; + constexpr const char OFFSET_TEXT[] = "helhello"; + constexpr int OFFSET_TEXT_SIZE = sizeof(OFFSET_TEXT); + + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + + constexpr const char *TEST_FILE = "testdata/pread_pwrite.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::write(fd, HELLO, HELLO_SIZE), Succeeds(HELLO_SIZE)); + ASSERT_THAT(__llvm_libc::fsync(fd), Succeeds(0)); + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); + + fd = __llvm_libc::open(TEST_FILE, O_WRONLY); + ASSERT_EQ(errno, 0); + ASSERT_GT(fd, 0); + ASSERT_THAT(__llvm_libc::pwrite(fd, HELLO, HELLO_SIZE, OFFSET), + Succeeds(HELLO_SIZE)); + ASSERT_THAT(__llvm_libc::fsync(fd), Succeeds(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); + char read_buf[OFFSET_TEXT_SIZE]; + ASSERT_THAT(__llvm_libc::pread(fd, read_buf, HELLO_SIZE, OFFSET), + Succeeds(HELLO_SIZE)); + EXPECT_STREQ(read_buf, HELLO); + ASSERT_THAT(__llvm_libc::pread(fd, read_buf, OFFSET_TEXT_SIZE, 0), + Succeeds(OFFSET_TEXT_SIZE)); + EXPECT_STREQ(read_buf, OFFSET_TEXT); + ASSERT_THAT(__llvm_libc::close(fd), Succeeds(0)); + + ASSERT_THAT(__llvm_libc::unlink(TEST_FILE), Succeeds(0)); +} + +TEST(LlvmLibcUniStd, PWriteFails) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + EXPECT_THAT(__llvm_libc::pwrite(-1, "", 1, 0), Fails(EBADF)); +} + +TEST(LlvmLibcUniStd, PReadFails) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + EXPECT_THAT(__llvm_libc::pread(-1, nullptr, 1, 0), Fails(EBADF)); +}