mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
fuse update for 5.14
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCYORvYQAKCRDh3BK/laaZ PCfvAQCbU+PW2RbwlqjZMet6w9qorh29XYe786P5pNRVbMYCygD+N45l66Sbd/Rz 7M7ioVDseyTW4dnLhb8SzSNB0zr6jQs= =MDvD -----END PGP SIGNATURE----- Merge tag 'fuse-update-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse Pull fuse updates from Miklos Szeredi: - Fixes for virtiofs submounts - Misc fixes and cleanups * tag 'fuse-update-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: virtiofs: Fix spelling mistakes fuse: use DIV_ROUND_UP helper macro for calculations fuse: fix illegal access to inode with reused nodeid fuse: allow fallocate(FALLOC_FL_ZERO_RANGE) fuse: Make fuse_fill_super_submount() static fuse: Switch to fc_mount() for submounts fuse: Call vfs_get_tree() for submounts fuse: add dedicated filesystem context ops for submounts virtiofs: propagate sync() to file server fuse: reject internal errno fuse: check connected before queueing on fpq->io fuse: ignore PG_workingset after stealing fuse: Fix infinite loop in sget_fc() fuse: Fix crash if superblock of submount gets killed early fuse: Fix crash in fuse_dentry_automount() error path
This commit is contained in:
commit
8e4f3e1517
9 changed files with 161 additions and 81 deletions
|
@ -213,7 +213,7 @@ static int fuse_setup_one_mapping(struct inode *inode, unsigned long start_idx,
|
|||
dmap->writable = writable;
|
||||
if (!upgrade) {
|
||||
/*
|
||||
* We don't take a refernce on inode. inode is valid right now
|
||||
* We don't take a reference on inode. inode is valid right now
|
||||
* and when inode is going away, cleanup logic should first
|
||||
* cleanup dmap entries.
|
||||
*/
|
||||
|
@ -622,7 +622,7 @@ static int fuse_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
|
|||
}
|
||||
|
||||
/*
|
||||
* If read beyond end of file happnes, fs code seems to return
|
||||
* If read beyond end of file happens, fs code seems to return
|
||||
* it as hole
|
||||
*/
|
||||
iomap_hole:
|
||||
|
@ -1207,7 +1207,7 @@ static void fuse_dax_free_mem_worker(struct work_struct *work)
|
|||
ret);
|
||||
}
|
||||
|
||||
/* If number of free ranges are still below threhold, requeue */
|
||||
/* If number of free ranges are still below threshold, requeue */
|
||||
kick_dmap_free_worker(fcd, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ static void fuse_drop_waiting(struct fuse_conn *fc)
|
|||
{
|
||||
/*
|
||||
* lockess check of fc->connected is okay, because atomic_dec_and_test()
|
||||
* provides a memory barrier mached with the one in fuse_wait_aborted()
|
||||
* provides a memory barrier matched with the one in fuse_wait_aborted()
|
||||
* to ensure no wake-up is missed.
|
||||
*/
|
||||
if (atomic_dec_and_test(&fc->num_waiting) &&
|
||||
|
@ -783,6 +783,7 @@ static int fuse_check_page(struct page *page)
|
|||
1 << PG_uptodate |
|
||||
1 << PG_lru |
|
||||
1 << PG_active |
|
||||
1 << PG_workingset |
|
||||
1 << PG_reclaim |
|
||||
1 << PG_waiters))) {
|
||||
dump_page(page, "fuse: trying to steal weird page");
|
||||
|
@ -1271,6 +1272,15 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
|
|||
goto restart;
|
||||
}
|
||||
spin_lock(&fpq->lock);
|
||||
/*
|
||||
* Must not put request on fpq->io queue after having been shut down by
|
||||
* fuse_abort_conn()
|
||||
*/
|
||||
if (!fpq->connected) {
|
||||
req->out.h.error = err = -ECONNABORTED;
|
||||
goto out_end;
|
||||
|
||||
}
|
||||
list_add(&req->list, &fpq->io);
|
||||
spin_unlock(&fpq->lock);
|
||||
cs->req = req;
|
||||
|
@ -1857,7 +1867,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
|
|||
}
|
||||
|
||||
err = -EINVAL;
|
||||
if (oh.error <= -1000 || oh.error > 0)
|
||||
if (oh.error <= -512 || oh.error > 0)
|
||||
goto copy_finish;
|
||||
|
||||
spin_lock(&fpq->lock);
|
||||
|
|
|
@ -252,7 +252,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
|||
if (ret == -ENOMEM)
|
||||
goto out;
|
||||
if (ret || fuse_invalid_attr(&outarg.attr) ||
|
||||
inode_wrong_type(inode, outarg.attr.mode))
|
||||
fuse_stale_inode(inode, outarg.generation, &outarg.attr))
|
||||
goto invalid;
|
||||
|
||||
forget_all_cached_acls(inode);
|
||||
|
@ -309,68 +309,23 @@ static int fuse_dentry_delete(const struct dentry *dentry)
|
|||
static struct vfsmount *fuse_dentry_automount(struct path *path)
|
||||
{
|
||||
struct fs_context *fsc;
|
||||
struct fuse_mount *parent_fm = get_fuse_mount_super(path->mnt->mnt_sb);
|
||||
struct fuse_conn *fc = parent_fm->fc;
|
||||
struct fuse_mount *fm;
|
||||
struct vfsmount *mnt;
|
||||
struct fuse_inode *mp_fi = get_fuse_inode(d_inode(path->dentry));
|
||||
struct super_block *sb;
|
||||
int err;
|
||||
|
||||
fsc = fs_context_for_submount(path->mnt->mnt_sb->s_type, path->dentry);
|
||||
if (IS_ERR(fsc)) {
|
||||
err = PTR_ERR(fsc);
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(fsc))
|
||||
return ERR_CAST(fsc);
|
||||
|
||||
err = -ENOMEM;
|
||||
fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
|
||||
if (!fm)
|
||||
goto out_put_fsc;
|
||||
|
||||
fsc->s_fs_info = fm;
|
||||
sb = sget_fc(fsc, NULL, set_anon_super_fc);
|
||||
if (IS_ERR(sb)) {
|
||||
err = PTR_ERR(sb);
|
||||
kfree(fm);
|
||||
goto out_put_fsc;
|
||||
}
|
||||
fm->fc = fuse_conn_get(fc);
|
||||
|
||||
/* Initialize superblock, making @mp_fi its root */
|
||||
err = fuse_fill_super_submount(sb, mp_fi);
|
||||
if (err)
|
||||
goto out_put_sb;
|
||||
|
||||
sb->s_flags |= SB_ACTIVE;
|
||||
fsc->root = dget(sb->s_root);
|
||||
/* We are done configuring the superblock, so unlock it */
|
||||
up_write(&sb->s_umount);
|
||||
|
||||
down_write(&fc->killsb);
|
||||
list_add_tail(&fm->fc_entry, &fc->mounts);
|
||||
up_write(&fc->killsb);
|
||||
/* Pass the FUSE inode of the mount for fuse_get_tree_submount() */
|
||||
fsc->fs_private = mp_fi;
|
||||
|
||||
/* Create the submount */
|
||||
mnt = vfs_create_mount(fsc);
|
||||
if (IS_ERR(mnt)) {
|
||||
err = PTR_ERR(mnt);
|
||||
goto out_put_fsc;
|
||||
}
|
||||
mntget(mnt);
|
||||
mnt = fc_mount(fsc);
|
||||
if (!IS_ERR(mnt))
|
||||
mntget(mnt);
|
||||
|
||||
put_fs_context(fsc);
|
||||
return mnt;
|
||||
|
||||
out_put_sb:
|
||||
/*
|
||||
* Only jump here when fsc->root is NULL and sb is still locked
|
||||
* (otherwise put_fs_context() will put the superblock)
|
||||
*/
|
||||
deactivate_locked_super(sb);
|
||||
out_put_fsc:
|
||||
put_fs_context(fsc);
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
const struct dentry_operations fuse_dentry_operations = {
|
||||
|
|
|
@ -645,7 +645,7 @@ static ssize_t fuse_get_res_by_io(struct fuse_io_priv *io)
|
|||
* == bytes_transferred or rw == WRITE, the caller sets 'pos' to -1.
|
||||
*
|
||||
* An example:
|
||||
* User requested DIO read of 64K. It was splitted into two 32K fuse requests,
|
||||
* User requested DIO read of 64K. It was split into two 32K fuse requests,
|
||||
* both submitted asynchronously. The first of them was ACKed by userspace as
|
||||
* fully completed (req->out.args[0].size == 32K) resulting in pos == -1. The
|
||||
* second request was ACKed as short, e.g. only 1K was read, resulting in
|
||||
|
@ -1403,7 +1403,7 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
|
|||
nbytes += ret;
|
||||
|
||||
ret += start;
|
||||
npages = (ret + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
npages = DIV_ROUND_UP(ret, PAGE_SIZE);
|
||||
|
||||
ap->descs[ap->num_pages].offset = start;
|
||||
fuse_page_descs_length_init(ap->descs, ap->num_pages, npages);
|
||||
|
@ -2905,11 +2905,13 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
|
|||
};
|
||||
int err;
|
||||
bool lock_inode = !(mode & FALLOC_FL_KEEP_SIZE) ||
|
||||
(mode & FALLOC_FL_PUNCH_HOLE);
|
||||
(mode & (FALLOC_FL_PUNCH_HOLE |
|
||||
FALLOC_FL_ZERO_RANGE));
|
||||
|
||||
bool block_faults = FUSE_IS_DAX(inode) && lock_inode;
|
||||
|
||||
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
|
||||
if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
|
||||
FALLOC_FL_ZERO_RANGE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (fm->fc->no_fallocate)
|
||||
|
@ -2924,7 +2926,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (mode & FALLOC_FL_PUNCH_HOLE) {
|
||||
if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) {
|
||||
loff_t endbyte = offset + length - 1;
|
||||
|
||||
err = fuse_writeback_range(inode, offset, endbyte);
|
||||
|
@ -2964,7 +2966,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
|
|||
file_update_time(file);
|
||||
}
|
||||
|
||||
if (mode & FALLOC_FL_PUNCH_HOLE)
|
||||
if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
|
||||
truncate_pagecache_range(inode, offset, offset + length - 1);
|
||||
|
||||
fuse_invalidate_attr(inode);
|
||||
|
|
|
@ -761,6 +761,9 @@ struct fuse_conn {
|
|||
/* Auto-mount submounts announced by the server */
|
||||
unsigned int auto_submounts:1;
|
||||
|
||||
/* Propagate syncfs() to server */
|
||||
unsigned int sync_fs:1;
|
||||
|
||||
/** The number of requests waiting for completion */
|
||||
atomic_t num_waiting;
|
||||
|
||||
|
@ -867,6 +870,13 @@ static inline u64 fuse_get_attr_version(struct fuse_conn *fc)
|
|||
return atomic64_read(&fc->attr_version);
|
||||
}
|
||||
|
||||
static inline bool fuse_stale_inode(const struct inode *inode, int generation,
|
||||
struct fuse_attr *attr)
|
||||
{
|
||||
return inode->i_generation != generation ||
|
||||
inode_wrong_type(inode, attr->mode);
|
||||
}
|
||||
|
||||
static inline void fuse_make_bad(struct inode *inode)
|
||||
{
|
||||
remove_inode_hash(inode);
|
||||
|
@ -1081,15 +1091,6 @@ void fuse_send_init(struct fuse_mount *fm);
|
|||
*/
|
||||
int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx);
|
||||
|
||||
/*
|
||||
* Fill in superblock for submounts
|
||||
* @sb: partially-initialized superblock to fill in
|
||||
* @parent_fi: The fuse_inode of the parent filesystem where this submount is
|
||||
* mounted
|
||||
*/
|
||||
int fuse_fill_super_submount(struct super_block *sb,
|
||||
struct fuse_inode *parent_fi);
|
||||
|
||||
/*
|
||||
* Remove the mount from the connection
|
||||
*
|
||||
|
@ -1097,6 +1098,11 @@ int fuse_fill_super_submount(struct super_block *sb,
|
|||
*/
|
||||
bool fuse_mount_remove(struct fuse_mount *fm);
|
||||
|
||||
/*
|
||||
* Setup context ops for submounts
|
||||
*/
|
||||
int fuse_init_fs_context_submount(struct fs_context *fsc);
|
||||
|
||||
/*
|
||||
* Shut down the connection (possibly sending DESTROY request).
|
||||
*/
|
||||
|
|
100
fs/fuse/inode.c
100
fs/fuse/inode.c
|
@ -350,8 +350,8 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
|
|||
inode->i_generation = generation;
|
||||
fuse_init_inode(inode, attr);
|
||||
unlock_new_inode(inode);
|
||||
} else if (inode_wrong_type(inode, attr->mode)) {
|
||||
/* Inode has changed type, any I/O on the old should fail */
|
||||
} else if (fuse_stale_inode(inode, generation, attr)) {
|
||||
/* nodeid was reused, any I/O on the old inode should fail */
|
||||
fuse_make_bad(inode);
|
||||
iput(inode);
|
||||
goto retry;
|
||||
|
@ -506,6 +506,45 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int fuse_sync_fs(struct super_block *sb, int wait)
|
||||
{
|
||||
struct fuse_mount *fm = get_fuse_mount_super(sb);
|
||||
struct fuse_conn *fc = fm->fc;
|
||||
struct fuse_syncfs_in inarg;
|
||||
FUSE_ARGS(args);
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Userspace cannot handle the wait == 0 case. Avoid a
|
||||
* gratuitous roundtrip.
|
||||
*/
|
||||
if (!wait)
|
||||
return 0;
|
||||
|
||||
/* The filesystem is being unmounted. Nothing to do. */
|
||||
if (!sb->s_root)
|
||||
return 0;
|
||||
|
||||
if (!fc->sync_fs)
|
||||
return 0;
|
||||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
args.in_numargs = 1;
|
||||
args.in_args[0].size = sizeof(inarg);
|
||||
args.in_args[0].value = &inarg;
|
||||
args.opcode = FUSE_SYNCFS;
|
||||
args.nodeid = get_node_id(sb->s_root->d_inode);
|
||||
args.out_numargs = 0;
|
||||
|
||||
err = fuse_simple_request(fm, &args);
|
||||
if (err == -ENOSYS) {
|
||||
fc->sync_fs = 0;
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
enum {
|
||||
OPT_SOURCE,
|
||||
OPT_SUBTYPE,
|
||||
|
@ -909,6 +948,7 @@ static const struct super_operations fuse_super_operations = {
|
|||
.put_super = fuse_put_super,
|
||||
.umount_begin = fuse_umount_begin,
|
||||
.statfs = fuse_statfs,
|
||||
.sync_fs = fuse_sync_fs,
|
||||
.show_options = fuse_show_options,
|
||||
};
|
||||
|
||||
|
@ -1275,8 +1315,8 @@ static void fuse_sb_defaults(struct super_block *sb)
|
|||
sb->s_xattr = fuse_no_acl_xattr_handlers;
|
||||
}
|
||||
|
||||
int fuse_fill_super_submount(struct super_block *sb,
|
||||
struct fuse_inode *parent_fi)
|
||||
static int fuse_fill_super_submount(struct super_block *sb,
|
||||
struct fuse_inode *parent_fi)
|
||||
{
|
||||
struct fuse_mount *fm = get_fuse_mount_super(sb);
|
||||
struct super_block *parent_sb = parent_fi->inode.i_sb;
|
||||
|
@ -1313,6 +1353,58 @@ int fuse_fill_super_submount(struct super_block *sb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Filesystem context private data holds the FUSE inode of the mount point */
|
||||
static int fuse_get_tree_submount(struct fs_context *fsc)
|
||||
{
|
||||
struct fuse_mount *fm;
|
||||
struct fuse_inode *mp_fi = fsc->fs_private;
|
||||
struct fuse_conn *fc = get_fuse_conn(&mp_fi->inode);
|
||||
struct super_block *sb;
|
||||
int err;
|
||||
|
||||
fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
|
||||
if (!fm)
|
||||
return -ENOMEM;
|
||||
|
||||
fsc->s_fs_info = fm;
|
||||
sb = sget_fc(fsc, NULL, set_anon_super_fc);
|
||||
if (IS_ERR(sb)) {
|
||||
kfree(fm);
|
||||
return PTR_ERR(sb);
|
||||
}
|
||||
fm->fc = fuse_conn_get(fc);
|
||||
|
||||
/* Initialize superblock, making @mp_fi its root */
|
||||
err = fuse_fill_super_submount(sb, mp_fi);
|
||||
if (err) {
|
||||
fuse_conn_put(fc);
|
||||
kfree(fm);
|
||||
sb->s_fs_info = NULL;
|
||||
deactivate_locked_super(sb);
|
||||
return err;
|
||||
}
|
||||
|
||||
down_write(&fc->killsb);
|
||||
list_add_tail(&fm->fc_entry, &fc->mounts);
|
||||
up_write(&fc->killsb);
|
||||
|
||||
sb->s_flags |= SB_ACTIVE;
|
||||
fsc->root = dget(sb->s_root);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fs_context_operations fuse_context_submount_ops = {
|
||||
.get_tree = fuse_get_tree_submount,
|
||||
};
|
||||
|
||||
int fuse_init_fs_context_submount(struct fs_context *fsc)
|
||||
{
|
||||
fsc->ops = &fuse_context_submount_ops;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fuse_init_fs_context_submount);
|
||||
|
||||
int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
|
||||
{
|
||||
struct fuse_dev *fud = NULL;
|
||||
|
|
|
@ -200,9 +200,12 @@ static int fuse_direntplus_link(struct file *file,
|
|||
if (!d_in_lookup(dentry)) {
|
||||
struct fuse_inode *fi;
|
||||
inode = d_inode(dentry);
|
||||
if (inode && get_node_id(inode) != o->nodeid)
|
||||
inode = NULL;
|
||||
if (!inode ||
|
||||
get_node_id(inode) != o->nodeid ||
|
||||
inode_wrong_type(inode, o->attr.mode)) {
|
||||
fuse_stale_inode(inode, o->generation, &o->attr)) {
|
||||
if (inode)
|
||||
fuse_make_bad(inode);
|
||||
d_invalidate(dentry);
|
||||
dput(dentry);
|
||||
goto retry;
|
||||
|
|
|
@ -1447,6 +1447,7 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
|
|||
fc->release = fuse_free_conn;
|
||||
fc->delete_stale = true;
|
||||
fc->auto_submounts = true;
|
||||
fc->sync_fs = true;
|
||||
|
||||
/* Tell FUSE to split requests that exceed the virtqueue's size */
|
||||
fc->max_pages_limit = min_t(unsigned int, fc->max_pages_limit,
|
||||
|
@ -1496,6 +1497,9 @@ static int virtio_fs_init_fs_context(struct fs_context *fsc)
|
|||
{
|
||||
struct fuse_fs_context *ctx;
|
||||
|
||||
if (fsc->purpose == FS_CONTEXT_FOR_SUBMOUNT)
|
||||
return fuse_init_fs_context_submount(fsc);
|
||||
|
||||
ctx = kzalloc(sizeof(struct fuse_fs_context), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -181,6 +181,9 @@
|
|||
* - add FUSE_OPEN_KILL_SUIDGID
|
||||
* - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
|
||||
* - add FUSE_SETXATTR_ACL_KILL_SGID
|
||||
*
|
||||
* 7.34
|
||||
* - add FUSE_SYNCFS
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
|
@ -216,7 +219,7 @@
|
|||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 33
|
||||
#define FUSE_KERNEL_MINOR_VERSION 34
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
|
@ -509,6 +512,7 @@ enum fuse_opcode {
|
|||
FUSE_COPY_FILE_RANGE = 47,
|
||||
FUSE_SETUPMAPPING = 48,
|
||||
FUSE_REMOVEMAPPING = 49,
|
||||
FUSE_SYNCFS = 50,
|
||||
|
||||
/* CUSE specific operations */
|
||||
CUSE_INIT = 4096,
|
||||
|
@ -971,4 +975,8 @@ struct fuse_removemapping_one {
|
|||
#define FUSE_REMOVEMAPPING_MAX_ENTRY \
|
||||
(PAGE_SIZE / sizeof(struct fuse_removemapping_one))
|
||||
|
||||
struct fuse_syncfs_in {
|
||||
uint64_t padding;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_FUSE_H */
|
||||
|
|
Loading…
Reference in a new issue