From 7ef8c816073b486776352d26335b4fc67b720541 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sun, 12 Mar 2017 12:05:38 -0500 Subject: [PATCH] libc: socket fcntl flags inheritence Set O_NONBLOCK on sockets from the fcntl syscall and propagate this flag to socket_fs control files and sockets returned from accept. Fix #2318 --- repos/libports/include/libc-plugin/fd_alloc.h | 4 +- .../libports/src/lib/libc/socket_fs_plugin.cc | 48 ++++++++++++++++--- repos/libports/src/lib/libc/vfs_plugin.cc | 10 ++-- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/repos/libports/include/libc-plugin/fd_alloc.h b/repos/libports/include/libc-plugin/fd_alloc.h index 86d6fbb9e6..d1d4971b1e 100644 --- a/repos/libports/include/libc-plugin/fd_alloc.h +++ b/repos/libports/include/libc-plugin/fd_alloc.h @@ -43,8 +43,8 @@ namespace Libc { char const *fd_path = 0; /* for 'fchdir', 'fstat' */ Plugin *plugin = 0; Plugin_context *context = 0; - unsigned flags = 0; /* for 'fcntl' */ - unsigned status = 0; /* for 'fcntl' */ + int flags = 0; /* for 'fcntl' */ + bool cloexec = 0; /* for 'fcntl' */ Genode::Lock lock; void path(char const *newpath) diff --git a/repos/libports/src/lib/libc/socket_fs_plugin.cc b/repos/libports/src/lib/libc/socket_fs_plugin.cc index c83bd9dfce..499a2c4c8c 100644 --- a/repos/libports/src/lib/libc/socket_fs_plugin.cc +++ b/repos/libports/src/lib/libc/socket_fs_plugin.cc @@ -80,20 +80,28 @@ namespace { int _remote_fd = -1; int _local_fd = -1; + template + void _fd_apply(FUNC const &fn) + { + if (_data_fd != -1) fn(_data_fd); + if (_local_fd != -1) fn(_local_fd); + if (_remote_fd != -1) fn(_remote_fd); + } + + int _fd_flags = 0; + Socket_context(Absolute_path const &path) : path(path.base()) { } ~Socket_context() { - if (_data_fd != -1) close(_data_fd); - if (_local_fd != -1) close(_local_fd); - if (_remote_fd != -1) close(_remote_fd); + _fd_apply([] (int fd) { close(fd); }); } int _open_file(char const *file_name) { Absolute_path file(file_name, path.base()); - int const fd = open(file.base(), O_RDWR); + int const fd = open(file.base(), O_RDWR|_fd_flags); if (fd == -1) { Genode::error(__func__, ": ", file_name, " file not accessible"); throw Inaccessible(); @@ -101,6 +109,13 @@ namespace { return fd; } + int fd_flags() const { return _fd_flags; } + void fd_flags(int flags) + { + _fd_flags = flags; + _fd_apply([flags] (int fd) { fcntl(fd, F_SETFL, flags); }); + } + int data_fd() { if (_data_fd == -1) @@ -144,6 +159,7 @@ namespace { ssize_t read(Libc::File_descriptor *, void *, ::size_t) override; ssize_t write(Libc::File_descriptor *, const void *, ::size_t) override; + int fcntl(Libc::File_descriptor *, int, long) override; int close(Libc::File_descriptor *) override; int select(int, fd_set *, fd_set *, fd_set *, timeval *) override; }; @@ -367,7 +383,7 @@ extern "C" int socket_fs_accept(int libc_fd, sockaddr *addr, socklen_t *addrlen) char accept_socket[10]; { Absolute_path file("accept", context->path.base()); - int const fd = open(file.base(), O_RDONLY); + int const fd = open(file.base(), O_RDONLY|context->fd_flags()); if (fd == -1) { Genode::error(__func__, ": accept file not accessible"); return Errno(EINVAL); @@ -390,7 +406,7 @@ extern "C" int socket_fs_accept(int libc_fd, sockaddr *addr, socklen_t *addrlen) if (addr && addrlen) { Absolute_path file("remote", accept_path.base()); - int const fd = open(file.base(), O_RDONLY); + int const fd = open(file.base(), O_RDONLY|context->fd_flags()); if (fd == -1) { Genode::error(__func__, ": remote file not accessible"); return Errno(EINVAL); @@ -409,6 +425,9 @@ extern "C" int socket_fs_accept(int libc_fd, sockaddr *addr, socklen_t *addrlen) *addrlen = sizeof(remote_addr); } + /* inherit the O_NONBLOCK flag if set */ + accept_context->fd_flags(context->fd_flags()); + return accept_fd->libc_fd; } @@ -711,7 +730,22 @@ extern "C" int socket_fs_socket(int domain, int type, int protocol) ** File-plugin operations ** ****************************/ -//int Socket_plugin::fcntl(Libc::File_descriptor *fd, int cmd, long arg) override; +int Socket_plugin::fcntl(Libc::File_descriptor *fd, int cmd, long arg) +{ + Socket_context *context = dynamic_cast(fd->context); + if (!context) + return Errno(EINVAL); + + switch (cmd) { + case F_GETFL: + return context->fd_flags(); + case F_SETFL: + context->fd_flags(arg); return 0; + default: + Genode::error(__func__, " command ", cmd, " not supported on sockets"); + return Errno(EBADF); + } +} ssize_t Socket_plugin::read(Libc::File_descriptor *fd, void *buf, ::size_t count) { diff --git a/repos/libports/src/lib/libc/vfs_plugin.cc b/repos/libports/src/lib/libc/vfs_plugin.cc index da53041656..a63409b23e 100644 --- a/repos/libports/src/lib/libc/vfs_plugin.cc +++ b/repos/libports/src/lib/libc/vfs_plugin.cc @@ -190,7 +190,7 @@ Libc::File_descriptor *Libc::Vfs_plugin::open(char const *path, int flags, Libc::File_descriptor *fd = Libc::file_descriptor_allocator()->alloc(this, vfs_context(handle), libc_fd); - fd->status = flags; + fd->flags = flags & (O_NONBLOCK|O_APPEND); if ((flags & O_TRUNC) && (ftruncate(fd, 0) == -1)) { /* XXX leaking fd, missing errno */ @@ -632,9 +632,11 @@ int Libc::Vfs_plugin::fcntl(Libc::File_descriptor *fd, int cmd, long arg) return new_fd->libc_fd; } - case F_GETFD: return fd->flags; - case F_SETFD: fd->flags = arg; return 0; - case F_GETFL: return fd->status; + case F_GETFD: return fd->cloexec ? FD_CLOEXEC : 0; + case F_SETFD: fd->cloexec = arg == FD_CLOEXEC; return 0; + + case F_GETFL: return fd->flags; + case F_SETFL: fd->flags = arg; return 0; default: break;