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
|
* Introduced in version 2.8
|
||||||
*/
|
*/
|
||||||
int (*ioctl) (const char *, int cmd, void *arg,
|
int (*ioctl) (const char *fusepath,
|
||||||
struct fuse_file_info *, unsigned int flags, void *data);
|
int cmd,
|
||||||
|
void *arg,
|
||||||
|
struct fuse_file_info *ffi,
|
||||||
|
unsigned int flags,
|
||||||
|
void *data,
|
||||||
|
uint32_t *out_bufsz);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Poll for IO readiness events
|
* 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,
|
int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
|
||||||
uint64_t *idx);
|
uint64_t *idx);
|
||||||
int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
|
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,
|
int fuse_fs_poll(struct fuse_fs *fs, const char *path,
|
||||||
struct fuse_file_info *fi, struct fuse_pollhandle *ph,
|
struct fuse_file_info *fi, struct fuse_pollhandle *ph,
|
||||||
unsigned *reventsp);
|
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,
|
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;
|
fuse_get_context()->private_data = fs->user_data;
|
||||||
if (fs->op.ioctl) {
|
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",
|
fprintf(stderr, "ioctl[%llu] 0x%x flags: 0x%x\n",
|
||||||
(unsigned long long) fi->fh, cmd, flags);
|
(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
|
} else
|
||||||
return -ENOSYS;
|
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,
|
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,
|
struct fuse_file_info *llfi, unsigned int flags,
|
||||||
const void *in_buf, size_t in_bufsz,
|
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 *f = req_fuse_prepare(req);
|
||||||
struct fuse_intr_data d;
|
struct fuse_intr_data d;
|
||||||
struct fuse_file_info fi;
|
struct fuse_file_info fi;
|
||||||
char *path, *out_buf = NULL;
|
char *path, *out_buf = NULL;
|
||||||
int err;
|
int err;
|
||||||
|
uint32_t out_bufsz = out_bufsz_;
|
||||||
|
|
||||||
err = -EPERM;
|
err = -EPERM;
|
||||||
if (flags & FUSE_IOCTL_UNRESTRICTED)
|
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);
|
fuse_prepare_interrupt(f, req, &d);
|
||||||
|
|
||||||
err = fuse_fs_ioctl(f->fs, path, cmd, arg, &fi, flags,
|
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);
|
fuse_finish_interrupt(f, req, &d);
|
||||||
free_path(f, ino, path);
|
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
|
namespace fs
|
||||||
{
|
{
|
||||||
|
static
|
||||||
|
inline
|
||||||
|
int
|
||||||
|
ioctl(const int fd_,
|
||||||
|
const unsigned long request_)
|
||||||
|
{
|
||||||
|
return ::ioctl(fd_,request_);
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
inline
|
inline
|
||||||
int
|
int
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "dirinfo.hpp"
|
#include "dirinfo.hpp"
|
||||||
|
#include "endian.hpp"
|
||||||
#include "errno.hpp"
|
#include "errno.hpp"
|
||||||
#include "fileinfo.hpp"
|
#include "fileinfo.hpp"
|
||||||
#include "fs_base_close.hpp"
|
#include "fs_base_close.hpp"
|
||||||
|
@ -35,30 +36,81 @@
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
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
|
namespace l
|
||||||
{
|
{
|
||||||
static
|
static
|
||||||
int
|
int
|
||||||
ioctl(const int fd_,
|
ioctl(const int fd_,
|
||||||
const unsigned long cmd_,
|
const int cmd_,
|
||||||
void *data_)
|
void *data_,
|
||||||
|
uint32_t *out_bufsz_)
|
||||||
{
|
{
|
||||||
int rv;
|
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);
|
return ((rv == -1) ? -errno : rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
int
|
int
|
||||||
ioctl_file(fuse_file_info *ffi_,
|
ioctl_file(fuse_file_info *ffi_,
|
||||||
const unsigned long cmd_,
|
const int cmd_,
|
||||||
void *data_)
|
void *data_,
|
||||||
|
uint32_t *out_bufsz_)
|
||||||
{
|
{
|
||||||
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi_->fh);
|
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 Branches &branches_,
|
||||||
const uint64_t minfreespace_,
|
const uint64_t minfreespace_,
|
||||||
const char *fusepath_,
|
const char *fusepath_,
|
||||||
const unsigned long cmd_,
|
const int cmd_,
|
||||||
void *data_)
|
void *data_,
|
||||||
|
uint32_t *out_bufsz_)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
int rv;
|
int rv;
|
||||||
|
@ -91,7 +144,7 @@ namespace l
|
||||||
if(fd == -1)
|
if(fd == -1)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
rv = l::ioctl(fd,cmd_,data_);
|
rv = l::ioctl(fd,cmd_,data_,out_bufsz_);
|
||||||
|
|
||||||
fs::close(fd);
|
fs::close(fd);
|
||||||
|
|
||||||
|
@ -100,9 +153,10 @@ namespace l
|
||||||
|
|
||||||
static
|
static
|
||||||
int
|
int
|
||||||
ioctl_dir(fuse_file_info *ffi_,
|
ioctl_dir(fuse_file_info *ffi_,
|
||||||
const unsigned long cmd_,
|
const int cmd_,
|
||||||
void *data_)
|
void *data_,
|
||||||
|
uint32_t *out_bufsz_)
|
||||||
{
|
{
|
||||||
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
|
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
|
||||||
const fuse_context *fc = fuse_get_context();
|
const fuse_context *fc = fuse_get_context();
|
||||||
|
@ -115,7 +169,8 @@ namespace l
|
||||||
config.minfreespace,
|
config.minfreespace,
|
||||||
di->fusepath.c_str(),
|
di->fusepath.c_str(),
|
||||||
cmd_,
|
cmd_,
|
||||||
data_);
|
data_,
|
||||||
|
out_bufsz_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,12 +182,12 @@ namespace FUSE
|
||||||
void *arg_,
|
void *arg_,
|
||||||
fuse_file_info *ffi_,
|
fuse_file_info *ffi_,
|
||||||
unsigned int flags_,
|
unsigned int flags_,
|
||||||
void *data_)
|
void *data_,
|
||||||
|
uint32_t *out_bufsz_)
|
||||||
{
|
{
|
||||||
if(flags_ & FUSE_IOCTL_DIR)
|
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_,out_bufsz_);
|
||||||
return l::ioctl_file(ffi_,cmd_,data_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,5 +26,6 @@ namespace FUSE
|
||||||
void *arg_,
|
void *arg_,
|
||||||
fuse_file_info *ffi_,
|
fuse_file_info *ffi_,
|
||||||
unsigned int flags_,
|
unsigned int flags_,
|
||||||
void *data_);
|
void *data_,
|
||||||
|
uint32_t *out_bufsz_);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user