diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 520046f768b5d..fcf1278eae723 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -245,6 +245,9 @@ set(TARGET_LIBC_ENTRYPOINTS # https://github.com/llvm/llvm-project/issues/80060 # libc.src.sys.epoll.epoll_pwait2 + # sys/ioctl.h entrypoints + libc.src.sys.ioctl.ioctl + # sys/mman.h entrypoints libc.src.sys.mman.madvise libc.src.sys.mman.mincore diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index 7432a7e912e81..1161ae260be2e 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -172,6 +172,9 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.stdlib.free libc.src.stdlib.malloc + # sys/ioctl.h entrypoints + libc.src.sys.ioctl.ioctl + # sys/mman.h entrypoints libc.src.sys.mman.mmap libc.src.sys.mman.munmap diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 0b645a2d2fb8b..050fc2672a57e 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -246,6 +246,9 @@ set(TARGET_LIBC_ENTRYPOINTS # https://github.com/llvm/llvm-project/issues/80060 # libc.src.sys.epoll.epoll_pwait2 + # sys/ioctl.h entrypoints + libc.src.sys.ioctl.ioctl + # sys/mman.h entrypoints libc.src.sys.mman.madvise libc.src.sys.mman.mincore diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 9f447dd0d35d2..07ebf51f70774 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -246,6 +246,9 @@ set(TARGET_LIBC_ENTRYPOINTS # https://github.com/llvm/llvm-project/issues/80060 # libc.src.sys.epoll.epoll_pwait2 + # sys/ioctl.h entrypoints + libc.src.sys.ioctl.ioctl + # sys/mman.h entrypoints libc.src.sys.mman.madvise libc.src.sys.mman.mincore diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt index 209fcb965242f..1e9f59621a8e5 100644 --- a/libc/hdr/CMakeLists.txt +++ b/libc/hdr/CMakeLists.txt @@ -126,6 +126,15 @@ add_proxy_header_library( libc.include.llvm-libc-macros.sys_epoll_macros ) +add_proxy_header_library( + sys_ioctl_macros + HDRS + sys_ioctl_macros.h + FULL_BUILD_DEPENDS + libc.include.sys_ioctl + libc.include.llvm-libc-macros.sys_ioctl_macros +) + add_proxy_header_library( sys_stat_macros HDRS diff --git a/libc/hdr/sys_ioctl_macros.h b/libc/hdr/sys_ioctl_macros.h new file mode 100644 index 0000000000000..935d436273465 --- /dev/null +++ b/libc/hdr/sys_ioctl_macros.h @@ -0,0 +1,22 @@ +//===-- Definition of macros from sys/ioctl.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_HDR_SYS_IOCTL_MACROS_H +#define LLVM_LIBC_HDR_SYS_IOCTL_MACROS_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-macros/sys-ioctl-macros.h" + +#else // Overlay mode + +#include + +#endif // LLVM_LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_SYS_IOCTL_MACROS_H diff --git a/libc/include/llvm-libc-macros/linux/sys-ioctl-macros.h b/libc/include/llvm-libc-macros/linux/sys-ioctl-macros.h index 5eb779aeeca56..41226080084c3 100644 --- a/libc/include/llvm-libc-macros/linux/sys-ioctl-macros.h +++ b/libc/include/llvm-libc-macros/linux/sys-ioctl-macros.h @@ -15,5 +15,6 @@ // around the definitions of macros like _IO, _IOR, _IOW, and _IOWR that I don't // think is worth digging into right now. #define TIOCGETD 0x5424 +#define FIONREAD 0x541B #endif // LLVM_LIBC_MACROS_LINUX_SYS_IOCTL_MACROS_H diff --git a/libc/src/sys/CMakeLists.txt b/libc/src/sys/CMakeLists.txt index 9a73b80d35d2f..0fa11e9eee696 100644 --- a/libc/src/sys/CMakeLists.txt +++ b/libc/src/sys/CMakeLists.txt @@ -13,3 +13,4 @@ add_subdirectory(utsname) add_subdirectory(wait) add_subdirectory(prctl) add_subdirectory(uio) +add_subdirectory(ioctl) diff --git a/libc/src/sys/ioctl/CMakeLists.txt b/libc/src/sys/ioctl/CMakeLists.txt new file mode 100644 index 0000000000000..099a1b96389fc --- /dev/null +++ b/libc/src/sys/ioctl/CMakeLists.txt @@ -0,0 +1,10 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) +endif() + +add_entrypoint_object( + ioctl + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.ioctl +) diff --git a/libc/src/sys/ioctl/ioctl.h b/libc/src/sys/ioctl/ioctl.h new file mode 100644 index 0000000000000..62323ba7dd4dc --- /dev/null +++ b/libc/src/sys/ioctl/ioctl.h @@ -0,0 +1,20 @@ +//===-- Implementation header for ioctl ---------------------------*-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_IOCTL_IOCTL_H +#define LLVM_LIBC_SRC_SYS_IOCTL_IOCTL_H + +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +int ioctl(int fd, unsigned long request, ...); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SYS_IOCTL_IOCTL_H diff --git a/libc/src/sys/ioctl/linux/CMakeLists.txt b/libc/src/sys/ioctl/linux/CMakeLists.txt new file mode 100644 index 0000000000000..876f35aaee66c --- /dev/null +++ b/libc/src/sys/ioctl/linux/CMakeLists.txt @@ -0,0 +1,12 @@ +add_entrypoint_object( + ioctl + SRCS + ioctl.cpp + HDRS + ../ioctl.h + DEPENDS + libc.include.sys_ioctl + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) diff --git a/libc/src/sys/ioctl/linux/ioctl.cpp b/libc/src/sys/ioctl/linux/ioctl.cpp new file mode 100644 index 0000000000000..f03fea21c75bd --- /dev/null +++ b/libc/src/sys/ioctl/linux/ioctl.cpp @@ -0,0 +1,36 @@ +//===---------- Linux implementation of the ioctl function ----------------===// +// +// 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/ioctl/ioctl.h" + +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" +#include "src/errno/libc_errno.h" +#include +#include // For syscall numbers. + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, ioctl, (int fd, unsigned long request, ...)) { + va_list vargs; + va_start(vargs, request); + void *data_pointer = va_arg(vargs, void *); + int ret = + LIBC_NAMESPACE::syscall_impl(SYS_ioctl, fd, request, data_pointer); + va_end(vargs); + + // Some ioctls can be expected to return positive values + if (ret >= 0) + return ret; + + // If there is an error, errno is set and -1 is returned. + libc_errno = -ret; + return -1; +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/src/sys/CMakeLists.txt b/libc/test/src/sys/CMakeLists.txt index 224cc7905ad31..13bf91eef04be 100644 --- a/libc/test/src/sys/CMakeLists.txt +++ b/libc/test/src/sys/CMakeLists.txt @@ -13,3 +13,4 @@ add_subdirectory(auxv) add_subdirectory(epoll) add_subdirectory(uio) add_subdirectory(time) +add_subdirectory(ioctl) diff --git a/libc/test/src/sys/ioctl/CMakeLists.txt b/libc/test/src/sys/ioctl/CMakeLists.txt new file mode 100644 index 0000000000000..b4bbe81c92ff2 --- /dev/null +++ b/libc/test/src/sys/ioctl/CMakeLists.txt @@ -0,0 +1,3 @@ +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${LIBC_TARGET_OS}) +endif() diff --git a/libc/test/src/sys/ioctl/linux/CMakeLists.txt b/libc/test/src/sys/ioctl/linux/CMakeLists.txt new file mode 100644 index 0000000000000..e5095c54a729f --- /dev/null +++ b/libc/test/src/sys/ioctl/linux/CMakeLists.txt @@ -0,0 +1,17 @@ +add_custom_target(libc_sys_ioctl_unittests) + +add_libc_unittest( + ioctl_test + SUITE + libc_sys_ioctl_unittests + SRCS + ioctl_test.cpp + DEPENDS + libc.hdr.ioctl_macros + libc.src.sys.ioctl.ioctl + libc.src.errno.errno + libc.src.fcntl.open + libc.src.unistd.close + libc.src.unistd.read + libc.src.unistd.write +) diff --git a/libc/test/src/sys/ioctl/linux/ioctl_test.cpp b/libc/test/src/sys/ioctl/linux/ioctl_test.cpp new file mode 100644 index 0000000000000..9c56a4689b186 --- /dev/null +++ b/libc/test/src/sys/ioctl/linux/ioctl_test.cpp @@ -0,0 +1,75 @@ +//===-- Unittests for ioctl -----------------------------------------------===// +// +// 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/errno/libc_errno.h" +#include "src/fcntl/open.h" +#include "src/sys/ioctl/ioctl.h" +#include "src/unistd/close.h" +#include "src/unistd/read.h" +#include "src/unistd/write.h" + +#include "test/UnitTest/ErrnoSetterMatcher.h" +#include "test/UnitTest/Test.h" + +#include "hdr/sys_stat_macros.h" + +#include "hdr/sys_ioctl_macros.h" + +using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds; + +TEST(LlvmLibcSysIoctlTest, InvalidCommandAndFIONREAD) { + LIBC_NAMESPACE::libc_errno = 0; + + // Setup the test file + constexpr const char *TEST_FILE_NAME = "ioctl.test"; + constexpr const char TEST_MSG[] = "ioctl test"; + constexpr int TEST_MSG_SIZE = sizeof(TEST_MSG) - 1; + auto TEST_FILE = libc_make_test_file_path(TEST_FILE_NAME); + int new_test_file_fd = LIBC_NAMESPACE::open( + TEST_FILE, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + ASSERT_THAT( + (int)LIBC_NAMESPACE::write(new_test_file_fd, TEST_MSG, TEST_MSG_SIZE), + Succeeds(TEST_MSG_SIZE)); + ASSERT_ERRNO_SUCCESS(); + ASSERT_THAT(LIBC_NAMESPACE::close(new_test_file_fd), Succeeds(0)); + ASSERT_ERRNO_SUCCESS(); + + // Reopen the file for testing + int fd = LIBC_NAMESPACE::open(TEST_FILE, O_RDONLY); + ASSERT_ERRNO_SUCCESS(); + ASSERT_GT(fd, 0); + + // FIONREAD reports the number of available bytes to read for the passed fd + // This will report the full size of the file, as we haven't read anything yet + int n = -1; + int ret = LIBC_NAMESPACE::ioctl(fd, FIONREAD, &n); + ASSERT_ERRNO_SUCCESS(); + ASSERT_GT(ret, -1); + ASSERT_EQ(n, TEST_MSG_SIZE); + + // But if we read some bytes... + constexpr int READ_COUNT = 5; + char read_buffer[READ_COUNT]; + ASSERT_THAT((int)LIBC_NAMESPACE::read(fd, read_buffer, READ_COUNT), + Succeeds(READ_COUNT)); + + // ... n should have decreased by the number of bytes we've read + int n_after_reading = -1; + ret = LIBC_NAMESPACE::ioctl(fd, FIONREAD, &n_after_reading); + ASSERT_ERRNO_SUCCESS(); + ASSERT_GT(ret, -1); + ASSERT_EQ(n - READ_COUNT, n_after_reading); + + // 0xDEADBEEF is just a random nonexistent command; + // calling this should always fail with ENOTTY + ret = LIBC_NAMESPACE::ioctl(fd, 0xDEADBEEF, NULL); + ASSERT_ERRNO_EQ(ENOTTY); + ASSERT_EQ(ret, -1); + + ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0)); +}