diff options
author | Xu Zhang <simonzgx@gmail.com> | 2024-06-15 07:17:44 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-14 16:17:44 -0700 |
commit | 0b24b4706982907799c838d80e06b3aa08420549 (patch) | |
tree | 7943efb95051d5f516004ad1687883e4ab737909 /libc/src | |
parent | 98b117e4355d2ba7b672014aafc41b3f646fc72c (diff) | |
download | llvm-0b24b4706982907799c838d80e06b3aa08420549.zip llvm-0b24b4706982907799c838d80e06b3aa08420549.tar.gz llvm-0b24b4706982907799c838d80e06b3aa08420549.tar.bz2 |
[libc] Add the implementation of the fdopen function (#94186)
Fixes #93711 .
This patch implements the ``fdopen`` function. Given that ``fdopen``
internally calls ``fcntl``, the implementation of ``fcntl`` has been
moved to the ``__support/OSUtil``, where it serves as an internal public
function.
Diffstat (limited to 'libc/src')
-rw-r--r-- | libc/src/__support/File/linux/file.cpp | 58 | ||||
-rw-r--r-- | libc/src/__support/File/linux/file.h | 3 | ||||
-rw-r--r-- | libc/src/__support/OSUtil/fcntl.h | 17 | ||||
-rw-r--r-- | libc/src/__support/OSUtil/linux/CMakeLists.txt | 6 | ||||
-rw-r--r-- | libc/src/__support/OSUtil/linux/fcntl.cpp | 94 | ||||
-rw-r--r-- | libc/src/fcntl/linux/CMakeLists.txt | 5 | ||||
-rw-r--r-- | libc/src/fcntl/linux/fcntl.cpp | 74 | ||||
-rw-r--r-- | libc/src/stdio/CMakeLists.txt | 7 | ||||
-rw-r--r-- | libc/src/stdio/fdopen.h | 20 | ||||
-rw-r--r-- | libc/src/stdio/linux/CMakeLists.txt | 12 | ||||
-rw-r--r-- | libc/src/stdio/linux/fdopen.cpp | 25 |
11 files changed, 244 insertions, 77 deletions
diff --git a/libc/src/__support/File/linux/file.cpp b/libc/src/__support/File/linux/file.cpp index b84da64..00ff938 100644 --- a/libc/src/__support/File/linux/file.cpp +++ b/libc/src/__support/File/linux/file.cpp @@ -8,10 +8,10 @@ #include "file.h" -#include "src/__support/File/file.h" - #include "src/__support/CPP/new.h" +#include "src/__support/File/file.h" #include "src/__support/File/linux/lseekImpl.h" +#include "src/__support/OSUtil/fcntl.h" #include "src/__support/OSUtil/syscall.h" // For internal syscall function. #include "src/errno/libc_errno.h" // For error macros @@ -119,6 +119,60 @@ ErrorOr<File *> openfile(const char *path, const char *mode) { return file; } +ErrorOr<LinuxFile *> create_file_from_fd(int fd, const char *mode) { + using ModeFlags = File::ModeFlags; + ModeFlags modeflags = File::mode_flags(mode); + if (modeflags == 0) { + return Error(EINVAL); + } + + int fd_flags = internal::fcntl(fd, F_GETFL); + if (fd_flags == -1) { + return Error(EBADF); + } + + using OpenMode = File::OpenMode; + if (((fd_flags & O_ACCMODE) == O_RDONLY && + !(modeflags & static_cast<ModeFlags>(OpenMode::READ))) || + ((fd_flags & O_ACCMODE) == O_WRONLY && + !(modeflags & static_cast<ModeFlags>(OpenMode::WRITE)))) { + return Error(EINVAL); + } + + bool do_seek = false; + if ((modeflags & static_cast<ModeFlags>(OpenMode::APPEND)) && + !(fd_flags & O_APPEND)) { + do_seek = true; + if (internal::fcntl(fd, F_SETFL, + reinterpret_cast<void *>(fd_flags | O_APPEND)) == -1) { + return Error(EBADF); + } + } + + uint8_t *buffer; + { + AllocChecker ac; + buffer = new (ac) uint8_t[File::DEFAULT_BUFFER_SIZE]; + if (!ac) { + return Error(ENOMEM); + } + } + AllocChecker ac; + auto *file = new (ac) + LinuxFile(fd, buffer, File::DEFAULT_BUFFER_SIZE, _IOFBF, true, modeflags); + if (!ac) { + return Error(ENOMEM); + } + if (do_seek) { + auto result = file->seek(0, SEEK_END); + if (!result.has_value()) { + free(file); + return Error(result.error()); + } + } + return file; +} + int get_fileno(File *f) { auto *lf = reinterpret_cast<LinuxFile *>(f); return lf->get_fd(); diff --git a/libc/src/__support/File/linux/file.h b/libc/src/__support/File/linux/file.h index 24e71b1..7d3770e 100644 --- a/libc/src/__support/File/linux/file.h +++ b/libc/src/__support/File/linux/file.h @@ -29,4 +29,7 @@ public: int get_fd() const { return fd; } }; +// Create a File object and associate it with a fd. +ErrorOr<LinuxFile *> create_file_from_fd(int fd, const char *mode); + } // namespace LIBC_NAMESPACE diff --git a/libc/src/__support/OSUtil/fcntl.h b/libc/src/__support/OSUtil/fcntl.h new file mode 100644 index 0000000..d934545 --- /dev/null +++ b/libc/src/__support/OSUtil/fcntl.h @@ -0,0 +1,17 @@ +//===-- Implementation header of internal fcntl 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_FCNTL_H +#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_FCNTL_H + +namespace LIBC_NAMESPACE::internal { + +int fcntl(int fd, int cmd, void *arg = nullptr); + +} // namespace LIBC_NAMESPACE::internal + +#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_FCNTL_H diff --git a/libc/src/__support/OSUtil/linux/CMakeLists.txt b/libc/src/__support/OSUtil/linux/CMakeLists.txt index 9a55232..78b117f 100644 --- a/libc/src/__support/OSUtil/linux/CMakeLists.txt +++ b/libc/src/__support/OSUtil/linux/CMakeLists.txt @@ -8,6 +8,7 @@ add_object_library( linux_util SRCS exit.cpp + fcntl.cpp HDRS io.h syscall.h @@ -15,4 +16,9 @@ add_object_library( .${LIBC_TARGET_ARCHITECTURE}.linux_${LIBC_TARGET_ARCHITECTURE}_util libc.src.__support.common libc.src.__support.CPP.string_view + libc.src.errno.errno + libc.hdr.fcntl_macros + libc.hdr.types.struct_flock + libc.hdr.types.struct_flock64 + libc.hdr.types.struct_f_owner_ex ) diff --git a/libc/src/__support/OSUtil/linux/fcntl.cpp b/libc/src/__support/OSUtil/linux/fcntl.cpp new file mode 100644 index 0000000..7dc416a --- /dev/null +++ b/libc/src/__support/OSUtil/linux/fcntl.cpp @@ -0,0 +1,94 @@ +//===-- Implementation of internal fcntl ----------------------------------===// +// +// 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/OSUtil/fcntl.h" + +#include "hdr/fcntl_macros.h" +#include "hdr/types/struct_f_owner_ex.h" +#include "hdr/types/struct_flock.h" +#include "hdr/types/struct_flock64.h" +#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/common.h" +#include "src/errno/libc_errno.h" + +#include <stdarg.h> +#include <sys/syscall.h> // For syscall numbers. + +namespace LIBC_NAMESPACE::internal { + +int fcntl(int fd, int cmd, void *arg) { + switch (cmd) { + case F_OFD_SETLKW: { + struct flock *flk = reinterpret_cast<struct flock *>(arg); + // convert the struct to a flock64 + struct flock64 flk64; + flk64.l_type = flk->l_type; + flk64.l_whence = flk->l_whence; + flk64.l_start = flk->l_start; + flk64.l_len = flk->l_len; + flk64.l_pid = flk->l_pid; + // create a syscall + return LIBC_NAMESPACE::syscall_impl<int>(SYS_fcntl, fd, cmd, &flk64); + } + case F_OFD_GETLK: + case F_OFD_SETLK: { + struct flock *flk = reinterpret_cast<struct flock *>(arg); + // convert the struct to a flock64 + struct flock64 flk64; + flk64.l_type = flk->l_type; + flk64.l_whence = flk->l_whence; + flk64.l_start = flk->l_start; + flk64.l_len = flk->l_len; + flk64.l_pid = flk->l_pid; + // create a syscall + int retVal = LIBC_NAMESPACE::syscall_impl<int>(SYS_fcntl, fd, cmd, &flk64); + // On failure, return + if (retVal == -1) + return -1; + // Check for overflow, i.e. the offsets are not the same when cast + // to off_t from off64_t. + if (static_cast<off_t>(flk64.l_len) != flk64.l_len || + static_cast<off_t>(flk64.l_start) != flk64.l_start) { + libc_errno = EOVERFLOW; + return -1; + } + // Now copy back into flk, in case flk64 got modified + flk->l_type = flk64.l_type; + flk->l_whence = flk64.l_whence; + flk->l_start = flk64.l_start; + flk->l_len = flk64.l_len; + flk->l_pid = flk64.l_pid; + return retVal; + } + case F_GETOWN: { + struct f_owner_ex fex; + int retVal = + LIBC_NAMESPACE::syscall_impl<int>(SYS_fcntl, fd, F_GETOWN_EX, &fex); + if (retVal == -EINVAL) + return LIBC_NAMESPACE::syscall_impl<int>(SYS_fcntl, fd, cmd, + reinterpret_cast<void *>(arg)); + if (static_cast<unsigned long>(retVal) <= -4096UL) + return fex.type == F_OWNER_PGRP ? -fex.pid : fex.pid; + + libc_errno = -retVal; + return -1; + } + // The general case + default: { + int retVal = LIBC_NAMESPACE::syscall_impl<int>( + SYS_fcntl, fd, cmd, reinterpret_cast<void *>(arg)); + if (retVal >= 0) { + return retVal; + } + libc_errno = -retVal; + return -1; + } + } +} + +} // namespace LIBC_NAMESPACE::internal diff --git a/libc/src/fcntl/linux/CMakeLists.txt b/libc/src/fcntl/linux/CMakeLists.txt index 732b7be..ee8ae63 100644 --- a/libc/src/fcntl/linux/CMakeLists.txt +++ b/libc/src/fcntl/linux/CMakeLists.txt @@ -18,12 +18,7 @@ add_entrypoint_object( ../fcntl.h DEPENDS libc.include.fcntl - libc.hdr.types.struct_flock - libc.hdr.types.struct_flock64 - libc.hdr.types.struct_f_owner_ex - libc.hdr.fcntl_macros libc.src.__support.OSUtil.osutil - libc.src.errno.errno ) add_entrypoint_object( diff --git a/libc/src/fcntl/linux/fcntl.cpp b/libc/src/fcntl/linux/fcntl.cpp index 24a20fb..3875889 100644 --- a/libc/src/fcntl/linux/fcntl.cpp +++ b/libc/src/fcntl/linux/fcntl.cpp @@ -8,86 +8,20 @@ #include "src/fcntl/fcntl.h" -#include "hdr/fcntl_macros.h" -#include "hdr/types/struct_f_owner_ex.h" -#include "hdr/types/struct_flock.h" -#include "hdr/types/struct_flock64.h" -#include "src/__support/OSUtil/syscall.h" // For internal syscall function. +#include "src/__support/OSUtil/fcntl.h" #include "src/__support/common.h" -#include "src/errno/libc_errno.h" #include <stdarg.h> -#include <sys/syscall.h> // For syscall numbers. -// The OFD file locks require special handling for LARGEFILES namespace LIBC_NAMESPACE { + LLVM_LIBC_FUNCTION(int, fcntl, (int fd, int cmd, ...)) { void *arg; va_list varargs; va_start(varargs, cmd); arg = va_arg(varargs, void *); va_end(varargs); - - switch (cmd) { - case F_SETLKW: - return syscall_impl<int>(SYS_fcntl, fd, cmd, arg); - case F_OFD_SETLKW: { - struct flock *flk = reinterpret_cast<struct flock *>(arg); - // convert the struct to a flock64 - struct flock64 flk64; - flk64.l_type = flk->l_type; - flk64.l_whence = flk->l_whence; - flk64.l_start = flk->l_start; - flk64.l_len = flk->l_len; - flk64.l_pid = flk->l_pid; - // create a syscall - return syscall_impl<int>(SYS_fcntl, fd, cmd, &flk64); - } - case F_OFD_GETLK: - case F_OFD_SETLK: { - struct flock *flk = reinterpret_cast<struct flock *>(arg); - // convert the struct to a flock64 - struct flock64 flk64; - flk64.l_type = flk->l_type; - flk64.l_whence = flk->l_whence; - flk64.l_start = flk->l_start; - flk64.l_len = flk->l_len; - flk64.l_pid = flk->l_pid; - // create a syscall - int retVal = syscall_impl<int>(SYS_fcntl, fd, cmd, &flk64); - // On failure, return - if (retVal == -1) - return -1; - // Check for overflow, i.e. the offsets are not the same when cast - // to off_t from off64_t. - if (static_cast<off_t>(flk64.l_len) != flk64.l_len || - static_cast<off_t>(flk64.l_start) != flk64.l_start) { - libc_errno = EOVERFLOW; - return -1; - } - // Now copy back into flk, in case flk64 got modified - flk->l_type = flk64.l_type; - flk->l_whence = flk64.l_whence; - flk->l_start = flk64.l_start; - flk->l_len = flk64.l_len; - flk->l_pid = flk64.l_pid; - return retVal; - } - case F_GETOWN: { - struct f_owner_ex fex; - int retVal = syscall_impl<int>(SYS_fcntl, fd, F_GETOWN_EX, &fex); - if (retVal == -EINVAL) - return syscall_impl<int>(SYS_fcntl, fd, cmd, - reinterpret_cast<void *>(arg)); - if (static_cast<unsigned long>(retVal) <= -4096UL) - return fex.type == F_OWNER_PGRP ? -fex.pid : fex.pid; - - libc_errno = -retVal; - return -1; - } - // The general case - default: - return syscall_impl<int>(SYS_fcntl, fd, cmd, reinterpret_cast<void *>(arg)); - } + return LIBC_NAMESPACE::internal::fcntl(fd, cmd, arg); } + } // namespace LIBC_NAMESPACE diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt index 7cf3278..a659d9e 100644 --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -230,6 +230,13 @@ add_entrypoint_object( .${LIBC_TARGET_OS}.rename ) +add_entrypoint_object( + fdopen + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.fdopen +) + # These entrypoints have multiple potential implementations. add_stdio_entrypoint_object(feof) add_stdio_entrypoint_object(feof_unlocked) diff --git a/libc/src/stdio/fdopen.h b/libc/src/stdio/fdopen.h new file mode 100644 index 0000000..158a133e --- /dev/null +++ b/libc/src/stdio/fdopen.h @@ -0,0 +1,20 @@ +//===-- Implementation header of open ---------------------------*- 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_STDIO_FDOPEN_H +#define LLVM_LIBC_SRC_STDIO_FDOPEN_H + +#include <stdio.h> + +namespace LIBC_NAMESPACE { + +FILE *fdopen(int fd, const char *mode); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_STDIO_FDOPEN_H diff --git a/libc/src/stdio/linux/CMakeLists.txt b/libc/src/stdio/linux/CMakeLists.txt index a08ff0b..fa36732 100644 --- a/libc/src/stdio/linux/CMakeLists.txt +++ b/libc/src/stdio/linux/CMakeLists.txt @@ -24,3 +24,15 @@ add_entrypoint_object( libc.src.__support.OSUtil.osutil libc.src.errno.errno ) + +add_entrypoint_object( + fdopen + SRCS + fdopen.cpp + HDRS + ../fdopen.h + DEPENDS + libc.include.stdio + libc.src.__support.File.file + libc.src.__support.File.platform_file +) diff --git a/libc/src/stdio/linux/fdopen.cpp b/libc/src/stdio/linux/fdopen.cpp new file mode 100644 index 0000000..a1d08ee --- /dev/null +++ b/libc/src/stdio/linux/fdopen.cpp @@ -0,0 +1,25 @@ +//===-- Implementation of fdopen --------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "src/stdio/fdopen.h" + +#include "src/__support/File/linux/file.h" +#include "src/errno/libc_errno.h" + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(::FILE *, fdopen, (int fd, const char *mode)) { + auto result = LIBC_NAMESPACE::create_file_from_fd(fd, mode); + if (!result.has_value()) { + libc_errno = result.error(); + return nullptr; + } + return reinterpret_cast<::FILE *>(result.value()); +} + +} // namespace LIBC_NAMESPACE |