Skip to content

[libc] Add the implementation of the fdopen function #94186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdio.snprintf
libc.src.stdio.vsprintf
libc.src.stdio.vsnprintf
libc.src.stdio.fdopen
#libc.src.stdio.sscanf
#libc.src.stdio.scanf
#libc.src.stdio.fscanf
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdio.sscanf
libc.src.stdio.scanf
libc.src.stdio.fscanf
libc.src.stdio.fdopen

# sys/mman.h entrypoints
libc.src.sys.mman.madvise
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdio.scanf
libc.src.stdio.fscanf
libc.src.stdio.fileno
libc.src.stdio.fdopen

# sys/epoll.h entrypoints
libc.src.sys.epoll.epoll_create
Expand Down
5 changes: 5 additions & 0 deletions libc/spec/posix.td
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,11 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<IntType>,
[ArgSpec<FILEPtr>]
>,
FunctionSpec<
"fdopen",
RetValSpec<FILEPtr>,
[ArgSpec<IntType>, ArgSpec<ConstCharPtr>]
>,
]
>;

Expand Down
58 changes: 56 additions & 2 deletions libc/src/__support/File/linux/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions libc/src/__support/File/linux/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ class LinuxFile : public File {
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
17 changes: 17 additions & 0 deletions libc/src/__support/OSUtil/fcntl.h
Original file line number Diff line number Diff line change
@@ -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
6 changes: 6 additions & 0 deletions libc/src/__support/OSUtil/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ add_object_library(
linux_util
SRCS
exit.cpp
fcntl.cpp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fcntl should be a separate target.

HDRS
io.h
syscall.h
DEPENDS
.${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
)
94 changes: 94 additions & 0 deletions libc/src/__support/OSUtil/linux/fcntl.cpp
Original file line number Diff line number Diff line change
@@ -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;
Comment on lines +63 to +64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this patch. Note though that it did break the 32b ARM build bot:
https://lab.llvm.org/buildbot/#/builders/182/builds/54

/llvm/libc_worker/worker/libc-arm32-debian/libc-arm32-debian-dbg/llvm-project/libc/src/__support/OSUtil/linux/fcntl.cpp:63:26: error: implicit conversion loses integer precision: '__off64_t' (aka 'long long') to '__off_t' (aka 'long') [-Werror,-Wshorten-64-to-32]
    flk->l_start = flk64.l_start;
                 ~ ~~~~~~^~~~~~~
/llvm/libc_worker/worker/libc-arm32-debian/libc-arm32-debian-dbg/llvm-project/libc/src/__support/OSUtil/linux/fcntl.cpp:64:24: error: implicit conversion loses integer precision: '__off64_t' (aka 'long long') to '__off_t' (aka 'long') [-Werror,-Wshorten-64-to-32]
    flk->l_len = flk64.l_len;
               ~ ~~~~~~^~~~~

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-4096UL while left hand side is unsigned?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Michael seems to have asked the same question, see #89507

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by this logic. If the return result is negative but malformed, should we return errors instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also we should use MAX_ERRNO instead of hard-coded value here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused by this logic. If the return result is negative but malformed, should we return errors instead?

Frankly, I'm also puzzled by this code. I found the same code in GLIBC, yet I'm also uncertain as to why the GLIBC's authors chose this way. Perhaps we could set it aside for now, and I'll open a new issue to address it after I delve into it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're only moving the function to different location, it's fine for me to leave it as-is. But please create an issue for further investigation / fixing and add a TODO here. Thanks,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're only moving the function to different location, it's fine for me to leave it as-is. But please create an issue for further investigation / fixing and add a TODO here. Thanks,

Please help assign #95570 to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We really should not be copying code from ANY libc which we do not understand.

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
5 changes: 0 additions & 5 deletions libc/src/fcntl/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
74 changes: 4 additions & 70 deletions libc/src/fcntl/linux/fcntl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 7 additions & 0 deletions libc/src/stdio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,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)
Expand Down
20 changes: 20 additions & 0 deletions libc/src/stdio/fdopen.h
Original file line number Diff line number Diff line change
@@ -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>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michaelrj-google : shall we create an issue to add proxy header for FILE?


namespace LIBC_NAMESPACE {

FILE *fdopen(int fd, const char *mode);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_STDIO_FDOPEN_H
Loading
Loading