libc: improve dup/dup2 in vfs_plugin

This patch replaces the naive dup2 implementation (that merely
duplicated the context pointer) by the replication of the original
FD state by re-opening the same file with the same flags and seek
position. This prevents a potential double release of the VFS handle
(the FD context). It also implements 'dup'.

Fixes #3505
Fixes #3477
This commit is contained in:
Norman Feske 2019-09-23 18:27:46 +02:00 committed by Christian Helmuth
parent 59c60b8031
commit c8b7710e5d
3 changed files with 48 additions and 5 deletions

View File

@ -268,7 +268,7 @@ extern "C" int dup2(int libc_fd, int new_libc_fd)
close(new_libc_fd);
new_fd = file_descriptor_allocator()->alloc(fd->plugin, 0, new_libc_fd);
new_fd->path(fd->fd_path);
/* new_fd->context must be assigned by the plugin implementing 'dup2' */
return fd->plugin->dup2(fd, new_fd);
}

View File

@ -86,6 +86,7 @@ class Libc::Vfs_plugin : public Plugin
int access(char const *, int) override;
int close(File_descriptor *) override;
File_descriptor *dup(File_descriptor *) override;
int dup2(File_descriptor *, File_descriptor *) override;
int fcntl(File_descriptor *, int, long) override;
int fstat(File_descriptor *, struct stat *) override;

View File

@ -425,11 +425,55 @@ int Libc::Vfs_plugin::close(File_descriptor *fd)
int Libc::Vfs_plugin::dup2(File_descriptor *fd,
File_descriptor *new_fd)
{
new_fd->context = fd->context;
Vfs::Vfs_handle *handle = nullptr;
typedef Vfs::Directory_service::Open_result Result;
if (VFS_THREAD_SAFE(_root_dir.open(fd->fd_path, fd->flags, &handle, _alloc))
!= Result::OPEN_OK) {
warning("dup2 failed for path ", fd->fd_path);
return Errno(EBADF);
}
handle->seek(vfs_handle(fd)->seek());
handle->handler(&_response_handler);
new_fd->context = vfs_context(handle);
new_fd->flags = fd->flags;
new_fd->path(fd->fd_path);
return new_fd->libc_fd;
}
Libc::File_descriptor *Libc::Vfs_plugin::dup(File_descriptor *fd)
{
Vfs::Vfs_handle *handle = nullptr;
typedef Vfs::Directory_service::Open_result Result;
if (VFS_THREAD_SAFE(_root_dir.open(fd->fd_path, fd->flags, &handle, _alloc))
!= Result::OPEN_OK) {
warning("dup failed for path ", fd->fd_path);
errno = EBADF;
return nullptr;
}
handle->seek(vfs_handle(fd)->seek());
handle->handler(&_response_handler);
File_descriptor * const new_fd =
file_descriptor_allocator()->alloc(this, vfs_context(handle));
new_fd->flags = fd->flags;
new_fd->path(fd->fd_path);
return new_fd;
}
int Libc::Vfs_plugin::fstat(File_descriptor *fd, struct stat *buf)
{
Vfs::Vfs_handle *handle = vfs_handle(fd);
@ -990,13 +1034,11 @@ int Libc::Vfs_plugin::fcntl(File_descriptor *fd, int cmd, long arg)
file_descriptor_allocator()->alloc(this, 0);
if (!new_fd) return Errno(EMFILE);
new_fd->path(fd->fd_path);
/*
* Use new allocated number as name of file descriptor
* duplicate.
*/
if (dup2(fd, new_fd) == -1) {
if (Vfs_plugin::dup2(fd, new_fd) == -1) {
error("Plugin::fcntl: dup2 unexpectedly failed");
return Errno(EINVAL);
}