mirror of
https://github.com/trapexit/mergerfs.git
synced 2024-11-26 02:09:52 +08:00
Merge pull request #581 from trapexit/ioctl
fix FS_IOC_{GET,SET}{FLAGS,VERSION} ioctl calls
This commit is contained in:
commit
e8622fdd64
|
@ -505,8 +505,13 @@ struct fuse_operations {
|
|||
*
|
||||
* Introduced in version 2.8
|
||||
*/
|
||||
int (*ioctl) (const char *, int cmd, void *arg,
|
||||
struct fuse_file_info *, unsigned int flags, void *data);
|
||||
int (*ioctl) (const char *fusepath,
|
||||
int cmd,
|
||||
void *arg,
|
||||
struct fuse_file_info *ffi,
|
||||
unsigned int flags,
|
||||
void *data,
|
||||
uint32_t *out_bufsz);
|
||||
|
||||
/**
|
||||
* Poll for IO readiness events
|
||||
|
@ -884,7 +889,8 @@ int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
|
|||
int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
|
||||
uint64_t *idx);
|
||||
int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
|
||||
struct fuse_file_info *fi, unsigned int flags, void *data);
|
||||
struct fuse_file_info *fi, unsigned int flags,
|
||||
void *data, uint32_t *out_bufsz);
|
||||
int fuse_fs_poll(struct fuse_fs *fs, const char *path,
|
||||
struct fuse_file_info *fi, struct fuse_pollhandle *ph,
|
||||
unsigned *reventsp);
|
||||
|
|
|
@ -2202,7 +2202,8 @@ int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
|
|||
}
|
||||
|
||||
int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
|
||||
struct fuse_file_info *fi, unsigned int flags, void *data)
|
||||
struct fuse_file_info *fi, unsigned int flags,
|
||||
void *data, uint32_t *out_size)
|
||||
{
|
||||
fuse_get_context()->private_data = fs->user_data;
|
||||
if (fs->op.ioctl) {
|
||||
|
@ -2210,7 +2211,7 @@ int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
|
|||
fprintf(stderr, "ioctl[%llu] 0x%x flags: 0x%x\n",
|
||||
(unsigned long long) fi->fh, cmd, flags);
|
||||
|
||||
return fs->op.ioctl(path, cmd, arg, fi, flags, data);
|
||||
return fs->op.ioctl(path, cmd, arg, fi, flags, data, out_size);
|
||||
} else
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
@ -3896,13 +3897,14 @@ static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
|
|||
static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
|
||||
struct fuse_file_info *llfi, unsigned int flags,
|
||||
const void *in_buf, size_t in_bufsz,
|
||||
size_t out_bufsz)
|
||||
size_t out_bufsz_)
|
||||
{
|
||||
struct fuse *f = req_fuse_prepare(req);
|
||||
struct fuse_intr_data d;
|
||||
struct fuse_file_info fi;
|
||||
char *path, *out_buf = NULL;
|
||||
int err;
|
||||
uint32_t out_bufsz = out_bufsz_;
|
||||
|
||||
err = -EPERM;
|
||||
if (flags & FUSE_IOCTL_UNRESTRICTED)
|
||||
|
@ -3931,7 +3933,7 @@ static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
|
|||
fuse_prepare_interrupt(f, req, &d);
|
||||
|
||||
err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags,
|
||||
out_buf ?: (void *)in_buf);
|
||||
out_buf ?: (void *)in_buf, &out_bufsz);
|
||||
|
||||
fuse_finish_interrupt(f, req, &d);
|
||||
free_path(f, ino, path);
|
||||
|
|
32
src/endian.hpp
Normal file
32
src/endian.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace endian
|
||||
{
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_big(void)
|
||||
{
|
||||
const union { uint32_t i; char c[4]; } u = { 0x01000000 };
|
||||
|
||||
return u.c[0];
|
||||
}
|
||||
};
|
|
@ -22,6 +22,15 @@
|
|||
|
||||
namespace fs
|
||||
{
|
||||
static
|
||||
inline
|
||||
int
|
||||
ioctl(const int fd_,
|
||||
const unsigned long request_)
|
||||
{
|
||||
return ::ioctl(fd_,request_);
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
int
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "config.hpp"
|
||||
#include "dirinfo.hpp"
|
||||
#include "endian.hpp"
|
||||
#include "errno.hpp"
|
||||
#include "fileinfo.hpp"
|
||||
#include "fs_base_close.hpp"
|
||||
|
@ -35,30 +36,81 @@
|
|||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
#ifndef FS_IOC_GETFLAGS
|
||||
# define FS_IOC_GETFLAGS _IOR('f',1,long)
|
||||
#endif
|
||||
|
||||
#ifndef FS_IOC_SETFLAGS
|
||||
# define FS_IOC_SETFLAGS _IOW('f',2,long)
|
||||
#endif
|
||||
|
||||
#ifndef FS_IOC_GETVERSION
|
||||
# define FS_IOC_GETVERSION _IOR('v',1,long)
|
||||
#endif
|
||||
|
||||
#ifndef FS_IOC_SETVERSION
|
||||
# define FS_IOC_SETVERSION _IOW('v',2,long)
|
||||
#endif
|
||||
|
||||
/*
|
||||
There is a bug with FUSE and these ioctl commands. The regular
|
||||
libfuse high level API assumes the output buffer size based on the
|
||||
command and gives no control over this. FS_IOC_GETFLAGS and
|
||||
FS_IOC_SETFLAGS however are defined as `long` when in fact it is an
|
||||
`int`. On 64bit systems where long is 8 bytes this can lead to
|
||||
libfuse telling the kernel to write 8 bytes and if the user only
|
||||
allocated an integer then it will overwrite the 4 bytes after the
|
||||
variable which could result in data corruption and/or crashes.
|
||||
|
||||
I've modified the API to allow changing of the output buffer
|
||||
size. This fixes the issue on little endian systems because the
|
||||
lower 4 bytes are the same regardless of what the user
|
||||
allocated. However, on big endian systems thats not the case and it
|
||||
is not possible to safely handle the situation.
|
||||
|
||||
https://lwn.net/Articles/575846/
|
||||
*/
|
||||
|
||||
namespace l
|
||||
{
|
||||
static
|
||||
int
|
||||
ioctl(const int fd_,
|
||||
const unsigned long cmd_,
|
||||
void *data_)
|
||||
ioctl(const int fd_,
|
||||
const int cmd_,
|
||||
void *data_,
|
||||
uint32_t *out_bufsz_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = fs::ioctl(fd_,cmd_,data_);
|
||||
switch(cmd_)
|
||||
{
|
||||
case FS_IOC_GETFLAGS:
|
||||
case FS_IOC_SETFLAGS:
|
||||
case FS_IOC_GETVERSION:
|
||||
case FS_IOC_SETVERSION:
|
||||
if(endian::is_big() && (sizeof(long) != sizeof(int)))
|
||||
return -ENOTTY;
|
||||
*out_bufsz_ = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = ((_IOC_DIR(cmd_) == _IOC_NONE) ?
|
||||
fs::ioctl(fd_,cmd_) :
|
||||
fs::ioctl(fd_,cmd_,data_));
|
||||
|
||||
return ((rv == -1) ? -errno : rv);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
ioctl_file(fuse_file_info *ffi_,
|
||||
const unsigned long cmd_,
|
||||
void *data_)
|
||||
ioctl_file(fuse_file_info *ffi_,
|
||||
const int cmd_,
|
||||
void *data_,
|
||||
uint32_t *out_bufsz_)
|
||||
{
|
||||
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi_->fh);
|
||||
|
||||
return l::ioctl(fi->fd,cmd_,data_);
|
||||
return l::ioctl(fi->fd,cmd_,data_,out_bufsz_);
|
||||
}
|
||||
|
||||
|
||||
|
@ -72,8 +124,9 @@ namespace l
|
|||
const Branches &branches_,
|
||||
const uint64_t minfreespace_,
|
||||
const char *fusepath_,
|
||||
const unsigned long cmd_,
|
||||
void *data_)
|
||||
const int cmd_,
|
||||
void *data_,
|
||||
uint32_t *out_bufsz_)
|
||||
{
|
||||
int fd;
|
||||
int rv;
|
||||
|
@ -91,7 +144,7 @@ namespace l
|
|||
if(fd == -1)
|
||||
return -errno;
|
||||
|
||||
rv = l::ioctl(fd,cmd_,data_);
|
||||
rv = l::ioctl(fd,cmd_,data_,out_bufsz_);
|
||||
|
||||
fs::close(fd);
|
||||
|
||||
|
@ -100,9 +153,10 @@ namespace l
|
|||
|
||||
static
|
||||
int
|
||||
ioctl_dir(fuse_file_info *ffi_,
|
||||
const unsigned long cmd_,
|
||||
void *data_)
|
||||
ioctl_dir(fuse_file_info *ffi_,
|
||||
const int cmd_,
|
||||
void *data_,
|
||||
uint32_t *out_bufsz_)
|
||||
{
|
||||
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
|
||||
const fuse_context *fc = fuse_get_context();
|
||||
|
@ -115,7 +169,8 @@ namespace l
|
|||
config.minfreespace,
|
||||
di->fusepath.c_str(),
|
||||
cmd_,
|
||||
data_);
|
||||
data_,
|
||||
out_bufsz_);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,12 +182,12 @@ namespace FUSE
|
|||
void *arg_,
|
||||
fuse_file_info *ffi_,
|
||||
unsigned int flags_,
|
||||
void *data_)
|
||||
void *data_,
|
||||
uint32_t *out_bufsz_)
|
||||
{
|
||||
if(flags_ & FUSE_IOCTL_DIR)
|
||||
return l::ioctl_dir(ffi_,cmd_,data_);
|
||||
return l::ioctl_dir(ffi_,cmd_,data_,out_bufsz_);
|
||||
|
||||
|
||||
return l::ioctl_file(ffi_,cmd_,data_);
|
||||
return l::ioctl_file(ffi_,cmd_,data_,out_bufsz_);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,5 +26,6 @@ namespace FUSE
|
|||
void *arg_,
|
||||
fuse_file_info *ffi_,
|
||||
unsigned int flags_,
|
||||
void *data_);
|
||||
void *data_,
|
||||
uint32_t *out_bufsz_);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user