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 @@ -100,6 +100,9 @@ libc.src.sys.mman.mmap libc.src.sys.mman.munmap + # sys/sendfile entrypoints + libc.src.sys.sendfile.sendfile + # sys/stat.h entrypoints libc.src.sys.stat.chmod libc.src.sys.stat.fchmod 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 @@ -274,3 +274,7 @@ def SysStatAPI : PublicAPI<"sys/stat.h"> { let Types = ["mode_t"]; } + +def SysSendfileAPI : PublicAPI<"sys/sendfile.h"> { + let Types = ["off_t", "size_t", "ssize_t"]; +} 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 @@ -100,6 +100,9 @@ libc.src.sys.mman.mmap libc.src.sys.mman.munmap + # sys/sendfile entrypoints + libc.src.sys.sendfile.sendfile + # sys/stat.h entrypoints libc.src.sys.stat.chmod libc.src.sys.stat.fchmod diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -212,6 +212,16 @@ .llvm-libc-types.mode_t ) +add_gen_header( + sys_sendfile + DEF_FILE sys/sendfile.h.def + GEN_HDR sys/sendfile.h + DEPENDS + .llvm_libc_common_h + .llvm-libc-types.off_t + .llvm-libc-types.ssize_t +) + add_gen_header( sys_syscall DEF_FILE sys/syscall.h.def diff --git a/libc/include/sys/sendfile.h.def b/libc/include/sys/sendfile.h.def new file mode 100644 --- /dev/null +++ b/libc/include/sys/sendfile.h.def @@ -0,0 +1,16 @@ +//===-- Linux sys/sendfile.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_SYS_SENDFILE_H +#define LLVM_LIBC_SYS_SENDFILE_H + +#include <__llvm-libc-common.h> + +%%public_api() + +#endif // LLVM_LIBC_SYS_SENDFILE_H diff --git a/libc/spec/gnu_ext.td b/libc/spec/gnu_ext.td --- a/libc/spec/gnu_ext.td +++ b/libc/spec/gnu_ext.td @@ -130,11 +130,26 @@ ] >; + HeaderSpec SendFile = HeaderSpec< + "sys/sendfile.h", + [], // Macros + [OffTType, SizeTType, SSizeTType,], // Types + [], // Enumerations + [ + FunctionSpec< + "sendfile", + RetValSpec, + [ArgSpec, ArgSpec, ArgSpec, ArgSpec] + >, + ] + >; + let Headers = [ CType, FEnv, Math, PThread, + SendFile, StdIO, String, ]; diff --git a/libc/spec/posix.td b/libc/spec/posix.td --- a/libc/spec/posix.td +++ b/libc/spec/posix.td @@ -32,8 +32,6 @@ ConstType ConstRestrictedCharPtr = ConstType; NamedType ModeTType = NamedType<"mode_t">; - NamedType OffTType = NamedType<"off_t">; - NamedType SSizeTType = NamedType<"ssize_t">; NamedType PThreadAttrTType = NamedType<"pthread_attr_t">; PtrType PThreadAttrTPtr = PtrType; diff --git a/libc/spec/spec.td b/libc/spec/spec.td --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -63,6 +63,10 @@ def IntMaxTType : NamedType<"intmax_t">; def UIntMaxTType : NamedType<"uintmax_t">; +def OffTType : NamedType<"off_t">; +def OffTPtr : PtrType; +def SSizeTType : NamedType<"ssize_t">; + // _Noreturn is really not a type, but it is convenient to treat it as a type. def NoReturn : NamedType<"_Noreturn void">; diff --git a/libc/src/sys/CMakeLists.txt b/libc/src/sys/CMakeLists.txt --- a/libc/src/sys/CMakeLists.txt +++ b/libc/src/sys/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(mman) +add_subdirectory(sendfile) add_subdirectory(stat) diff --git a/libc/src/sys/sendfile/CMakeLists.txt b/libc/src/sys/sendfile/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/sys/sendfile/CMakeLists.txt @@ -0,0 +1,11 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) +endif() + +add_entrypoint_object( + sendfile + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.sendfile +) + diff --git a/libc/src/sys/sendfile/linux/CMakeLists.txt b/libc/src/sys/sendfile/linux/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/sys/sendfile/linux/CMakeLists.txt @@ -0,0 +1,12 @@ +add_entrypoint_object( + sendfile + SRCS + sendfile.cpp + HDRS + ../sendfile.h + DEPENDS + libc.include.sys_sendfile + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) diff --git a/libc/src/sys/sendfile/linux/sendfile.cpp b/libc/src/sys/sendfile/linux/sendfile.cpp new file mode 100644 --- /dev/null +++ b/libc/src/sys/sendfile/linux/sendfile.cpp @@ -0,0 +1,30 @@ +//===-- Linux implementation of sendfile ----------------------------------===// +// +// 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/sys/sendfile/sendfile.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(ssize_t, sendfile, + (int out_fd, int in_fd, off_t *offset, size_t count)) { + long ret = __llvm_libc::syscall(SYS_sendfile, in_fd, out_fd, offset, count); + if (ret < 0) { + errno = -ret; + return -1; + } + return ret; +} + +} // namespace __llvm_libc diff --git a/libc/src/sys/sendfile/sendfile.h b/libc/src/sys/sendfile/sendfile.h new file mode 100644 --- /dev/null +++ b/libc/src/sys/sendfile/sendfile.h @@ -0,0 +1,20 @@ +//===-- Implementation header for sendfile ----------------------*- 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_SYS_STAT_SENDFILE_H +#define LLVM_LIBC_SRC_SYS_STAT_SENDFILE_H + +#include + +namespace __llvm_libc { + +ssize_t sendfile(int, int, off_t *, size_t); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SYS_STAT_SENDFILE_H diff --git a/libc/test/src/sys/CMakeLists.txt b/libc/test/src/sys/CMakeLists.txt --- a/libc/test/src/sys/CMakeLists.txt +++ b/libc/test/src/sys/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(mman) +add_subdirectory(sendfile) add_subdirectory(stat) diff --git a/libc/test/src/sys/sendfile/CMakeLists.txt b/libc/test/src/sys/sendfile/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/sendfile/CMakeLists.txt @@ -0,0 +1,21 @@ +add_libc_testsuite(libc_sys_sendfile_unittests) + +add_subdirectory(testdata) + +add_libc_unittest( + sendfile_test + SUITE + libc_sys_sendfile_unittests + SRCS + sendfile_test.cpp + DEPENDS + libc.include.errno + libc.include.fcntl + libc.include.sys_stat + libc.src.fcntl.open + libc.src.sys.sendfile.sendfile + libc.src.unistd.close + libc.src.unistd.read + libc.src.unistd.unlink + libc.src.unistd.write +) diff --git a/libc/test/src/sys/sendfile/sendfile_test.cpp b/libc/test/src/sys/sendfile/sendfile_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/sendfile/sendfile_test.cpp @@ -0,0 +1,67 @@ +//===-- Unittests for sendfile --------------------------------------------===// +// +// 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/__support/CPP/string_view.h" +#include "src/fcntl/open.h" +#include "src/sys/sendfile/sendfile.h" +#include "src/unistd/close.h" +#include "src/unistd/read.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 +#include +#include + +namespace cpp = __llvm_libc::cpp; + +TEST(LlvmLibcSendfileTest, CreateAndTransfer) { + using __llvm_libc::testing::ErrnoSetterMatcher::Fails; + using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds; + + // The test strategy is to + // 1. Create a temporary file with known data. + // 2. Use sendfile to copy it to another file. + // 3. Make sure that the data was actually copied. + // 4. Clean up the temporary files. + constexpr const char *IN_FILE = "testdata/sendfile_in.test"; + constexpr const char *OUT_FILE = "testdata/sendfile_out.test"; + const char IN_DATA[] = "sendfile test"; + constexpr ssize_t IN_SIZE = ssize_t(sizeof(IN_DATA)); + errno = 0; + + int in_fd = __llvm_libc::open(IN_FILE, O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GT(in_fd, 0); + ASSERT_EQ(errno, 0); + ASSERT_EQ(__llvm_libc::write(in_fd, IN_DATA, IN_SIZE), IN_SIZE); + ASSERT_THAT(__llvm_libc::close(in_fd), Succeeds(0)); + + in_fd = __llvm_libc::open(IN_FILE, O_RDONLY); + ASSERT_GT(in_fd, 0); + ASSERT_EQ(errno, 0); + int out_fd = __llvm_libc::open(OUT_FILE, O_CREAT | O_WRONLY, S_IRWXU); + ASSERT_GT(out_fd, 0); + ASSERT_EQ(errno, 0); + ssize_t size = __llvm_libc::sendfile(in_fd, out_fd, nullptr, IN_SIZE); + ASSERT_EQ(size, IN_SIZE); + ASSERT_THAT(__llvm_libc::close(in_fd), Succeeds(0)); + ASSERT_THAT(__llvm_libc::close(out_fd), Succeeds(0)); + + out_fd = __llvm_libc::open(OUT_FILE, O_RDONLY); + ASSERT_GT(out_fd, 0); + ASSERT_EQ(errno, 0); + char buf[IN_SIZE]; + ASSERT_EQ(IN_SIZE, __llvm_libc::read(out_fd, buf, IN_SIZE)); + ASSERT_EQ(cpp::string_view(buf), cpp::string_view(IN_DATA)); + + ASSERT_THAT(__llvm_libc::unlink(IN_FILE), Succeeds(0)); + ASSERT_THAT(__llvm_libc::unlink(OUT_FILE), Succeeds(0)); +} diff --git a/libc/test/src/sys/sendfile/testdata/CMakeLists.txt b/libc/test/src/sys/sendfile/testdata/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/sys/sendfile/testdata/CMakeLists.txt @@ -0,0 +1 @@ +# Directory to hold temporary files created by tests.