[RFC PATCH 5/9] fuse: move fuse connection flags to the separate structure

From: Alexander Mikhalitsyn
Date: Mon Feb 20 2023 - 14:38:51 EST


Let's move all the fuse connection flags that can be safely zeroed
after connection reinitialization to the separate structure fuse_conn_flags.

All of these flags values are calculated dynamically basing on
the userspace daemon capabilities (like no_open, no_flush) or on the
response for FUSE_INIT request.

Cc: Miklos Szeredi <mszeredi@xxxxxxxxxx>
Cc: Al Viro <viro@xxxxxxxxxxxxxxxxxx>
Cc: Amir Goldstein <amir73il@xxxxxxxxx>
Cc: Stéphane Graber <stgraber@xxxxxxxxxx>
Cc: Seth Forshee <sforshee@xxxxxxxxxx>
Cc: Christian Brauner <brauner@xxxxxxxxxx>
Cc: Andrei Vagin <avagin@xxxxxxxxx>
Cc: Pavel Tikhomirov <ptikhomirov@xxxxxxxxxxxxx>
Cc: linux-fsdevel@xxxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
Cc: criu@xxxxxxxxxx
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@xxxxxxxxxxxxx>
---
fs/fuse/acl.c | 6 +-
fs/fuse/dev.c | 4 +-
fs/fuse/dir.c | 26 +++---
fs/fuse/file.c | 80 ++++++++---------
fs/fuse/fuse_i.h | 225 ++++++++++++++++++++++++----------------------
fs/fuse/inode.c | 52 +++++------
fs/fuse/readdir.c | 8 +-
fs/fuse/xattr.c | 18 ++--
8 files changed, 215 insertions(+), 204 deletions(-)

diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c
index a4850aee2639..b27953739de4 100644
--- a/fs/fuse/acl.c
+++ b/fs/fuse/acl.c
@@ -25,7 +25,7 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
if (fuse_is_bad(inode))
return ERR_PTR(-EIO);

- if (!fc->posix_acl || fc->no_getxattr)
+ if (!fc->posix_acl || fc->flags.no_getxattr)
return NULL;

if (type == ACL_TYPE_ACCESS)
@@ -42,7 +42,7 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
if (size > 0)
acl = posix_acl_from_xattr(fc->user_ns, value, size);
else if ((size == 0) || (size == -ENODATA) ||
- (size == -EOPNOTSUPP && fc->no_getxattr))
+ (size == -EOPNOTSUPP && fc->flags.no_getxattr))
acl = NULL;
else if (size == -ERANGE)
acl = ERR_PTR(-E2BIG);
@@ -64,7 +64,7 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
if (fuse_is_bad(inode))
return -EIO;

- if (!fc->posix_acl || fc->no_setxattr)
+ if (!fc->posix_acl || fc->flags.no_setxattr)
return -EOPNOTSUPP;

if (type == ACL_TYPE_ACCESS)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 85f69629f34d..737764c2295e 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -367,7 +367,7 @@ static void request_wait_answer(struct fuse_req *req)
struct fuse_iqueue *fiq = &fc->iq;
int err;

- if (!fc->no_interrupt) {
+ if (!fc->flags.no_interrupt) {
/* Any signal may interrupt this */
err = wait_event_interruptible(req->waitq,
test_bit(FR_FINISHED, &req->flags));
@@ -1901,7 +1901,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
if (nbytes != sizeof(struct fuse_out_header))
err = -EINVAL;
else if (oh.error == -ENOSYS)
- fc->no_interrupt = 1;
+ fc->flags.no_interrupt = 1;
else if (oh.error == -EAGAIN)
err = queue_interrupt(req);

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 49d91add08bc..63625c29d6ef 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -707,7 +707,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
} else {
file->private_data = ff;
fuse_finish_open(inode, file);
- if (fm->fc->atomic_o_trunc && trunc)
+ if (fm->fc->flags.atomic_o_trunc && trunc)
truncate_pagecache(inode, 0);
else if (!(ff->open_flags & FOPEN_KEEP_CACHE))
invalidate_inode_pages2(inode->i_mapping);
@@ -750,12 +750,12 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
/* Only creates */
file->f_mode |= FMODE_CREATED;

- if (fc->no_create)
+ if (fc->flags.no_create)
goto mknod;

err = fuse_create_open(dir, entry, file, flags, mode, FUSE_CREATE);
if (err == -ENOSYS) {
- fc->no_create = 1;
+ fc->flags.no_create = 1;
goto mknod;
}
out_dput:
@@ -1080,14 +1080,14 @@ static int fuse_rename2(struct user_namespace *mnt_userns, struct inode *olddir,
return -EINVAL;

if (flags) {
- if (fc->no_rename2 || fc->minor < 23)
+ if (fc->flags.no_rename2 || fc->minor < 23)
return -EINVAL;

err = fuse_rename_common(olddir, oldent, newdir, newent, flags,
FUSE_RENAME2,
sizeof(struct fuse_rename2_in));
if (err == -ENOSYS) {
- fc->no_rename2 = 1;
+ fc->flags.no_rename2 = 1;
err = -EINVAL;
}
} else {
@@ -1354,7 +1354,7 @@ static int fuse_access(struct inode *inode, int mask)

BUG_ON(mask & MAY_NOT_BLOCK);

- if (fm->fc->no_access)
+ if (fm->fc->flags.no_access)
return 0;

memset(&inarg, 0, sizeof(inarg));
@@ -1366,7 +1366,7 @@ static int fuse_access(struct inode *inode, int mask)
args.in_args[0].value = &inarg;
err = fuse_simple_request(fm, &args);
if (err == -ENOSYS) {
- fm->fc->no_access = 1;
+ fm->fc->flags.no_access = 1;
err = 0;
}
return err;
@@ -1503,7 +1503,7 @@ static const char *fuse_get_link(struct dentry *dentry, struct inode *inode,
if (fuse_is_bad(inode))
goto out_err;

- if (fc->cache_symlinks)
+ if (fc->flags.cache_symlinks)
return page_get_link(dentry, inode, callback);

err = -ECHILD;
@@ -1551,13 +1551,13 @@ static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end,
if (fuse_is_bad(inode))
return -EIO;

- if (fc->no_fsyncdir)
+ if (fc->flags.no_fsyncdir)
return 0;

inode_lock(inode);
err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNCDIR);
if (err == -ENOSYS) {
- fc->no_fsyncdir = 1;
+ fc->flags.no_fsyncdir = 1;
err = 0;
}
inode_unlock(inode);
@@ -1749,7 +1749,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
struct fuse_setattr_in inarg;
struct fuse_attr_out outarg;
bool is_truncate = false;
- bool is_wb = fc->writeback_cache && S_ISREG(inode->i_mode);
+ bool is_wb = fc->flags.writeback_cache && S_ISREG(inode->i_mode);
loff_t oldsize;
int err;
bool trust_local_cmtime = is_wb;
@@ -1782,7 +1782,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
/* This is coming from open(..., ... | O_TRUNC); */
WARN_ON(!(attr->ia_valid & ATTR_SIZE));
WARN_ON(attr->ia_size != 0);
- if (fc->atomic_o_trunc) {
+ if (fc->flags.atomic_o_trunc) {
/*
* No need to send request to userspace, since actual
* truncation has already been done by OPEN. But still
@@ -1929,7 +1929,7 @@ static int fuse_setattr(struct user_namespace *mnt_userns, struct dentry *entry,
*
* This should be done on write(), truncate() and chown().
*/
- if (!fc->handle_killpriv && !fc->handle_killpriv_v2) {
+ if (!fc->flags.handle_killpriv && !fc->handle_killpriv_v2) {
/*
* ia_mode calculation may have used stale i_mode.
* Refresh and recalculate.
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 6d89d4dd4c55..d5b30faff0b9 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -29,7 +29,7 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,

memset(&inarg, 0, sizeof(inarg));
inarg.flags = open_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
- if (!fm->fc->atomic_o_trunc)
+ if (!fm->fc->flags.atomic_o_trunc)
inarg.flags &= ~O_TRUNC;

if (fm->fc->handle_killpriv_v2 &&
@@ -110,7 +110,7 @@ static void fuse_file_put(struct fuse_file *ff, bool sync, bool isdir)
if (refcount_dec_and_test(&ff->count)) {
struct fuse_args *args = &ff->release_args->args;

- if (isdir ? ff->fm->fc->no_opendir : ff->fm->fc->no_open) {
+ if (isdir ? ff->fm->fc->flags.no_opendir : ff->fm->fc->flags.no_open) {
/* Do nothing when client does not implement 'open' */
fuse_release_end(ff->fm, args, 0);
} else if (sync) {
@@ -140,7 +140,7 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
ff->fh = 0;
/* Default for no-open */
ff->open_flags = FOPEN_KEEP_CACHE | (isdir ? FOPEN_CACHE_DIR : 0);
- if (isdir ? !fc->no_opendir : !fc->no_open) {
+ if (isdir ? !fc->flags.no_opendir : !fc->flags.no_open) {
struct fuse_open_out outarg;
int err;

@@ -154,9 +154,9 @@ struct fuse_file *fuse_file_open(struct fuse_mount *fm, u64 nodeid,
return ERR_PTR(err);
} else {
if (isdir)
- fc->no_opendir = 1;
+ fc->flags.no_opendir = 1;
else
- fc->no_open = 1;
+ fc->flags.no_open = 1;
}
}

@@ -205,7 +205,7 @@ void fuse_finish_open(struct inode *inode, struct file *file)
else if (ff->open_flags & FOPEN_NONSEEKABLE)
nonseekable_open(inode, file);

- if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC)) {
+ if (fc->flags.atomic_o_trunc && (file->f_flags & O_TRUNC)) {
struct fuse_inode *fi = get_fuse_inode(inode);

spin_lock(&fi->lock);
@@ -215,7 +215,7 @@ void fuse_finish_open(struct inode *inode, struct file *file)
file_update_time(file);
fuse_invalidate_attr_mask(inode, FUSE_STATX_MODSIZE);
}
- if ((file->f_mode & FMODE_WRITE) && fc->writeback_cache)
+ if ((file->f_mode & FMODE_WRITE) && fc->flags.writeback_cache)
fuse_link_write_file(file);
}

@@ -225,10 +225,10 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
struct fuse_conn *fc = fm->fc;
int err;
bool is_wb_truncate = (file->f_flags & O_TRUNC) &&
- fc->atomic_o_trunc &&
- fc->writeback_cache;
+ fc->flags.atomic_o_trunc &&
+ fc->flags.writeback_cache;
bool dax_truncate = (file->f_flags & O_TRUNC) &&
- fc->atomic_o_trunc && FUSE_IS_DAX(inode);
+ fc->flags.atomic_o_trunc && FUSE_IS_DAX(inode);

if (fuse_is_bad(inode))
return -EIO;
@@ -259,7 +259,7 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
if (!err) {
struct fuse_file *ff = file->private_data;

- if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC))
+ if (fc->flags.atomic_o_trunc && (file->f_flags & O_TRUNC))
truncate_pagecache(inode, 0);
else if (!(ff->open_flags & FOPEN_KEEP_CACHE))
invalidate_inode_pages2(inode->i_mapping);
@@ -350,7 +350,7 @@ static int fuse_release(struct inode *inode, struct file *file)
* Dirty pages might remain despite write_inode_now() call from
* fuse_flush() due to writes racing with the close.
*/
- if (fc->writeback_cache)
+ if (fc->flags.writeback_cache)
write_inode_now(inode, 1);

fuse_release_common(file, false);
@@ -505,12 +505,12 @@ static int fuse_do_flush(struct fuse_flush_args *fa)
goto out;

err = 0;
- if (fm->fc->no_flush)
+ if (fm->fc->flags.no_flush)
goto inval_attr_out;

err = fuse_simple_request(fm, &fa->args);
if (err == -ENOSYS) {
- fm->fc->no_flush = 1;
+ fm->fc->flags.no_flush = 1;
err = 0;
}

@@ -519,7 +519,7 @@ static int fuse_do_flush(struct fuse_flush_args *fa)
* In memory i_blocks is not maintained by fuse, if writeback cache is
* enabled, i_blocks from cached attr may not be accurate.
*/
- if (!err && fm->fc->writeback_cache)
+ if (!err && fm->fc->flags.writeback_cache)
fuse_invalidate_attr_mask(inode, STATX_BLOCKS);

out:
@@ -545,7 +545,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
if (fuse_is_bad(inode))
return -EIO;

- if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache)
+ if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->flags.writeback_cache)
return 0;

fa = kzalloc(sizeof(*fa), GFP_KERNEL);
@@ -628,12 +628,12 @@ static int fuse_fsync(struct file *file, loff_t start, loff_t end,
if (err)
goto out;

- if (fc->no_fsync)
+ if (fc->flags.no_fsync)
goto out;

err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNC);
if (err == -ENOSYS) {
- fc->no_fsync = 1;
+ fc->flags.no_fsync = 1;
err = 0;
}
out:
@@ -858,7 +858,7 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read,
* the file. Some data after the hole is in page cache, but has not
* reached the client fs yet. So the hole is not present there.
*/
- if (!fc->writeback_cache) {
+ if (!fc->flags.writeback_cache) {
loff_t pos = page_offset(ap->pages[0]) + num_read;
fuse_read_update_size(inode, pos, attr_ver);
}
@@ -989,7 +989,7 @@ static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file)

fuse_read_args_fill(ia, file, pos, count, FUSE_READ);
ia->read.attr_ver = fuse_get_attr_version(fm->fc);
- if (fm->fc->async_read) {
+ if (fm->fc->flags.async_read) {
ia->ff = fuse_file_get(ff);
ap->args.end = fuse_readpages_end;
err = fuse_simple_background(fm, &ap->args, GFP_KERNEL);
@@ -1056,7 +1056,7 @@ static ssize_t fuse_cache_read_iter(struct kiocb *iocb, struct iov_iter *to)
* Otherwise, only update if we attempt to read past EOF (to ensure
* i_size is up to date).
*/
- if (fc->auto_inval_data ||
+ if (fc->flags.auto_inval_data ||
(iocb->ki_pos + iov_iter_count(to) > i_size_read(inode))) {
int err;
err = fuse_update_attributes(inode, iocb->ki_filp, STATX_SIZE);
@@ -1263,7 +1263,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia,
ia->write.page_locked = true;
break;
}
- if (!fc->big_writes)
+ if (!fc->flags.big_writes)
break;
} while (iov_iter_count(ii) && count < fc->max_write &&
ap->num_pages < max_pages && offset == 0);
@@ -1343,7 +1343,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
struct fuse_conn *fc = get_fuse_conn(inode);
loff_t endbyte = 0;

- if (fc->writeback_cache) {
+ if (fc->flags.writeback_cache) {
/* Update size (EOF optimization) and mode (SUID clearing) */
err = fuse_update_attributes(mapping->host, file,
STATX_SIZE | STATX_MODE);
@@ -1864,7 +1864,7 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
* Do this only if writeback_cache is not enabled. If writeback_cache
* is enabled, we trust local ctime/mtime.
*/
- if (!fc->writeback_cache)
+ if (!fc->flags.writeback_cache)
fuse_invalidate_attr_mask(inode, FUSE_STATX_MODIFY);
spin_lock(&fi->lock);
rb_erase(&wpa->writepages_entry, &fi->writepages);
@@ -2368,7 +2368,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping,
loff_t fsize;
int err = -ENOMEM;

- WARN_ON(!fc->writeback_cache);
+ WARN_ON(!fc->flags.writeback_cache);

page = grab_cache_page_write_begin(mapping, index);
if (!page)
@@ -2641,13 +2641,13 @@ static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl)
if (cmd == F_CANCELLK) {
err = 0;
} else if (cmd == F_GETLK) {
- if (fc->no_lock) {
+ if (fc->flags.no_lock) {
posix_test_lock(file, fl);
err = 0;
} else
err = fuse_getlk(file, fl);
} else {
- if (fc->no_lock)
+ if (fc->flags.no_lock)
err = posix_lock_file(file, fl, NULL);
else
err = fuse_setlk(file, fl, 0);
@@ -2661,7 +2661,7 @@ static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl)
struct fuse_conn *fc = get_fuse_conn(inode);
int err;

- if (fc->no_flock) {
+ if (fc->flags.no_flock) {
err = locks_lock_file_wait(file, fl);
} else {
struct fuse_file *ff = file->private_data;
@@ -2683,7 +2683,7 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
struct fuse_bmap_out outarg;
int err;

- if (!inode->i_sb->s_bdev || fm->fc->no_bmap)
+ if (!inode->i_sb->s_bdev || fm->fc->flags.no_bmap)
return 0;

memset(&inarg, 0, sizeof(inarg));
@@ -2699,7 +2699,7 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
args.out_args[0].value = &outarg;
err = fuse_simple_request(fm, &args);
if (err == -ENOSYS)
- fm->fc->no_bmap = 1;
+ fm->fc->flags.no_bmap = 1;

return err ? 0 : outarg.block;
}
@@ -2718,7 +2718,7 @@ static loff_t fuse_lseek(struct file *file, loff_t offset, int whence)
struct fuse_lseek_out outarg;
int err;

- if (fm->fc->no_lseek)
+ if (fm->fc->flags.no_lseek)
goto fallback;

args.opcode = FUSE_LSEEK;
@@ -2732,7 +2732,7 @@ static loff_t fuse_lseek(struct file *file, loff_t offset, int whence)
err = fuse_simple_request(fm, &args);
if (err) {
if (err == -ENOSYS) {
- fm->fc->no_lseek = 1;
+ fm->fc->flags.no_lseek = 1;
goto fallback;
}
return err;
@@ -2839,7 +2839,7 @@ __poll_t fuse_file_poll(struct file *file, poll_table *wait)
FUSE_ARGS(args);
int err;

- if (fm->fc->no_poll)
+ if (fm->fc->flags.no_poll)
return DEFAULT_POLLMASK;

poll_wait(file, &ff->poll_wait, wait);
@@ -2867,7 +2867,7 @@ __poll_t fuse_file_poll(struct file *file, poll_table *wait)
if (!err)
return demangle_poll(outarg.revents);
if (err == -ENOSYS) {
- fm->fc->no_poll = 1;
+ fm->fc->flags.no_poll = 1;
return DEFAULT_POLLMASK;
}
return EPOLLERR;
@@ -2953,7 +2953,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
* By default, we want to optimize all I/Os with async request
* submission to the client filesystem if supported.
*/
- io->async = ff->fm->fc->async_dio;
+ io->async = ff->fm->fc->flags.async_dio;
io->iocb = iocb;
io->blocking = is_sync_kiocb(iocb);

@@ -3046,7 +3046,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
FALLOC_FL_ZERO_RANGE))
return -EOPNOTSUPP;

- if (fm->fc->no_fallocate)
+ if (fm->fc->flags.no_fallocate)
return -EOPNOTSUPP;

inode_lock(inode);
@@ -3086,7 +3086,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
args.in_args[0].value = &inarg;
err = fuse_simple_request(fm, &args);
if (err == -ENOSYS) {
- fm->fc->no_fallocate = 1;
+ fm->fc->flags.no_fallocate = 1;
err = -EOPNOTSUPP;
}
if (err)
@@ -3142,10 +3142,10 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
ssize_t err;
/* mark unstable when write-back is not used, and file_out gets
* extended */
- bool is_unstable = (!fc->writeback_cache) &&
+ bool is_unstable = (!fc->flags.writeback_cache) &&
((pos_out + len) > inode_out->i_size);

- if (fc->no_copy_file_range)
+ if (fc->flags.no_copy_file_range)
return -EOPNOTSUPP;

if (file_inode(file_in)->i_sb != file_inode(file_out)->i_sb)
@@ -3198,7 +3198,7 @@ static ssize_t __fuse_copy_file_range(struct file *file_in, loff_t pos_in,
args.out_args[0].value = &outarg;
err = fuse_simple_request(fm, &args);
if (err == -ENOSYS) {
- fc->no_copy_file_range = 1;
+ fc->flags.no_copy_file_range = 1;
err = -EOPNOTSUPP;
}
if (err)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index ce154e7ab715..4f4a6f912c7c 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -543,6 +543,122 @@ struct fuse_sync_bucket {
struct rcu_head rcu;
};

+/**
+ * A Fuse connection.
+ *
+ * This structure is created, when the root filesystem is mounted, and
+ * is destroyed, when the client device is closed and the last
+ * fuse_mount is destroyed.
+ */
+struct fuse_conn_flags {
+ /** Do readahead asynchronously? Only set in INIT */
+ unsigned async_read:1;
+
+ /** Do not send separate SETATTR request before open(O_TRUNC) */
+ unsigned atomic_o_trunc:1;
+
+ /** Filesystem supports NFS exporting. Only set in INIT */
+ unsigned export_support:1;
+
+ /** write-back cache policy (default is write-through) */
+ unsigned writeback_cache:1;
+
+ /** allow parallel lookups and readdir (default is serialized) */
+ unsigned parallel_dirops:1;
+
+ /** handle fs handles killing suid/sgid/cap on write/chown/trunc */
+ unsigned handle_killpriv:1;
+
+ /** cache READLINK responses in page cache */
+ unsigned cache_symlinks:1;
+
+ /*
+ * The following bitfields are only for optimization purposes
+ * and hence races in setting them will not cause malfunction
+ */
+
+ /** Is open/release not implemented by fs? */
+ unsigned no_open:1;
+
+ /** Is opendir/releasedir not implemented by fs? */
+ unsigned no_opendir:1;
+
+ /** Is fsync not implemented by fs? */
+ unsigned no_fsync:1;
+
+ /** Is fsyncdir not implemented by fs? */
+ unsigned no_fsyncdir:1;
+
+ /** Is flush not implemented by fs? */
+ unsigned no_flush:1;
+
+ /** Is setxattr not implemented by fs? */
+ unsigned no_setxattr:1;
+
+ /** Does file server support extended setxattr */
+ unsigned setxattr_ext:1;
+
+ /** Is getxattr not implemented by fs? */
+ unsigned no_getxattr:1;
+
+ /** Is listxattr not implemented by fs? */
+ unsigned no_listxattr:1;
+
+ /** Is removexattr not implemented by fs? */
+ unsigned no_removexattr:1;
+
+ /** Are posix file locking primitives not implemented by fs? */
+ unsigned no_lock:1;
+
+ /** Is access not implemented by fs? */
+ unsigned no_access:1;
+
+ /** Is create not implemented by fs? */
+ unsigned no_create:1;
+
+ /** Is interrupt not implemented by fs? */
+ unsigned no_interrupt:1;
+
+ /** Is bmap not implemented by fs? */
+ unsigned no_bmap:1;
+
+ /** Is poll not implemented by fs? */
+ unsigned no_poll:1;
+
+ /** Do multi-page cached writes */
+ unsigned big_writes:1;
+
+ /** Are BSD file locking primitives not implemented by fs? */
+ unsigned no_flock:1;
+
+ /** Is fallocate not implemented by fs? */
+ unsigned no_fallocate:1;
+
+ /** Is rename with flags implemented by fs? */
+ unsigned no_rename2:1;
+
+ /** Use enhanced/automatic page cache invalidation. */
+ unsigned auto_inval_data:1;
+
+ /** Filesystem is fully responsible for page cache invalidation. */
+ unsigned explicit_inval_data:1;
+
+ /** Does the filesystem support readdirplus? */
+ unsigned do_readdirplus:1;
+
+ /** Does the filesystem want adaptive readdirplus? */
+ unsigned readdirplus_auto:1;
+
+ /** Does the filesystem support asynchronous direct-IO submission? */
+ unsigned async_dio:1;
+
+ /** Is lseek not implemented by fs? */
+ unsigned no_lseek:1;
+
+ /** Does the filesystem support copy_file_range? */
+ unsigned no_copy_file_range:1;
+};
+
/**
* A Fuse connection.
*
@@ -641,30 +757,9 @@ struct fuse_conn {
/** Connection successful. Only set in INIT */
unsigned conn_init:1;

- /** Do readahead asynchronously? Only set in INIT */
- unsigned async_read:1;
-
/** Return an unique read error after abort. Only set in INIT */
unsigned abort_err:1;

- /** Do not send separate SETATTR request before open(O_TRUNC) */
- unsigned atomic_o_trunc:1;
-
- /** Filesystem supports NFS exporting. Only set in INIT */
- unsigned export_support:1;
-
- /** write-back cache policy (default is write-through) */
- unsigned writeback_cache:1;
-
- /** allow parallel lookups and readdir (default is serialized) */
- unsigned parallel_dirops:1;
-
- /** handle fs handles killing suid/sgid/cap on write/chown/trunc */
- unsigned handle_killpriv:1;
-
- /** cache READLINK responses in page cache */
- unsigned cache_symlinks:1;
-
/* show legacy mount options */
unsigned int legacy_opts_show:1;

@@ -676,92 +771,9 @@ struct fuse_conn {
*/
unsigned handle_killpriv_v2:1;

- /*
- * The following bitfields are only for optimization purposes
- * and hence races in setting them will not cause malfunction
- */
-
- /** Is open/release not implemented by fs? */
- unsigned no_open:1;
-
- /** Is opendir/releasedir not implemented by fs? */
- unsigned no_opendir:1;
-
- /** Is fsync not implemented by fs? */
- unsigned no_fsync:1;
-
- /** Is fsyncdir not implemented by fs? */
- unsigned no_fsyncdir:1;
-
- /** Is flush not implemented by fs? */
- unsigned no_flush:1;
-
- /** Is setxattr not implemented by fs? */
- unsigned no_setxattr:1;
-
- /** Does file server support extended setxattr */
- unsigned setxattr_ext:1;
-
- /** Is getxattr not implemented by fs? */
- unsigned no_getxattr:1;
-
- /** Is listxattr not implemented by fs? */
- unsigned no_listxattr:1;
-
- /** Is removexattr not implemented by fs? */
- unsigned no_removexattr:1;
-
- /** Are posix file locking primitives not implemented by fs? */
- unsigned no_lock:1;
-
- /** Is access not implemented by fs? */
- unsigned no_access:1;
-
- /** Is create not implemented by fs? */
- unsigned no_create:1;
-
- /** Is interrupt not implemented by fs? */
- unsigned no_interrupt:1;
-
- /** Is bmap not implemented by fs? */
- unsigned no_bmap:1;
-
- /** Is poll not implemented by fs? */
- unsigned no_poll:1;
-
- /** Do multi-page cached writes */
- unsigned big_writes:1;
-
/** Don't apply umask to creation modes */
unsigned dont_mask:1;

- /** Are BSD file locking primitives not implemented by fs? */
- unsigned no_flock:1;
-
- /** Is fallocate not implemented by fs? */
- unsigned no_fallocate:1;
-
- /** Is rename with flags implemented by fs? */
- unsigned no_rename2:1;
-
- /** Use enhanced/automatic page cache invalidation. */
- unsigned auto_inval_data:1;
-
- /** Filesystem is fully responsible for page cache invalidation. */
- unsigned explicit_inval_data:1;
-
- /** Does the filesystem support readdirplus? */
- unsigned do_readdirplus:1;
-
- /** Does the filesystem want adaptive readdirplus? */
- unsigned readdirplus_auto:1;
-
- /** Does the filesystem support asynchronous direct-IO submission? */
- unsigned async_dio:1;
-
- /** Is lseek not implemented by fs? */
- unsigned no_lseek:1;
-
/** Does the filesystem support posix acls? */
unsigned posix_acl:1;

@@ -771,9 +783,6 @@ struct fuse_conn {
/** Allow other than the mounter user to access the filesystem ? */
unsigned allow_other:1;

- /** Does the filesystem support copy_file_range? */
- unsigned no_copy_file_range:1;
-
/* Send DESTROY request */
unsigned int destroy:1;

@@ -804,6 +813,8 @@ struct fuse_conn {
/* Is tmpfile not implemented by fs? */
unsigned int no_tmpfile:1;

+ struct fuse_conn_flags flags;
+
/** The number of requests waiting for completion */
atomic_t num_waiting;

diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 33a108cfcefe..c3109e016494 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -224,7 +224,7 @@ u32 fuse_get_cache_mask(struct inode *inode)
{
struct fuse_conn *fc = get_fuse_conn(inode);

- if (!fc->writeback_cache || !S_ISREG(inode->i_mode))
+ if (!fc->flags.writeback_cache || !S_ISREG(inode->i_mode))
return 0;

return STATX_MTIME | STATX_CTIME | STATX_SIZE;
@@ -282,9 +282,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,

if (oldsize != attr->size) {
truncate_pagecache(inode, attr->size);
- if (!fc->explicit_inval_data)
+ if (!fc->flags.explicit_inval_data)
inval = true;
- } else if (fc->auto_inval_data) {
+ } else if (fc->flags.auto_inval_data) {
struct timespec64 new_mtime = {
.tv_sec = attr->mtime,
.tv_nsec = attr->mtimensec,
@@ -380,7 +380,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,

if ((inode->i_state & I_NEW)) {
inode->i_flags |= S_NOATIME;
- if (!fc->writeback_cache || !S_ISREG(attr->mode))
+ if (!fc->flags.writeback_cache || !S_ISREG(attr->mode))
inode->i_flags |= S_NOCMTIME;
inode->i_generation = generation;
fuse_init_inode(inode, attr);
@@ -459,7 +459,7 @@ bool fuse_lock_inode(struct inode *inode)
{
bool locked = false;

- if (!get_fuse_conn(inode)->parallel_dirops) {
+ if (!get_fuse_conn(inode)->flags.parallel_dirops) {
mutex_lock(&get_fuse_inode(inode)->mutex);
locked = true;
}
@@ -911,7 +911,7 @@ static struct dentry *fuse_get_dentry(struct super_block *sb,
struct fuse_entry_out outarg;
const struct qstr name = QSTR_INIT(".", 1);

- if (!fc->export_support)
+ if (!fc->flags.export_support)
goto out_err;

err = fuse_lookup_name(sb, handle->nodeid, &name, &outarg,
@@ -1011,7 +1011,7 @@ static struct dentry *fuse_get_parent(struct dentry *child)
struct fuse_entry_out outarg;
int err;

- if (!fc->export_support)
+ if (!fc->flags.export_support)
return ERR_PTR(-ESTALE);

err = fuse_lookup_name(child_inode->i_sb, get_node_id(child_inode),
@@ -1127,44 +1127,44 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,

ra_pages = arg->max_readahead / PAGE_SIZE;
if (flags & FUSE_ASYNC_READ)
- fc->async_read = 1;
+ fc->flags.async_read = 1;
if (!(flags & FUSE_POSIX_LOCKS))
- fc->no_lock = 1;
+ fc->flags.no_lock = 1;
if (arg->minor >= 17) {
if (!(flags & FUSE_FLOCK_LOCKS))
- fc->no_flock = 1;
+ fc->flags.no_flock = 1;
} else {
if (!(flags & FUSE_POSIX_LOCKS))
- fc->no_flock = 1;
+ fc->flags.no_flock = 1;
}
if (flags & FUSE_ATOMIC_O_TRUNC)
- fc->atomic_o_trunc = 1;
+ fc->flags.atomic_o_trunc = 1;
if (arg->minor >= 9) {
/* LOOKUP has dependency on proto version */
if (flags & FUSE_EXPORT_SUPPORT)
- fc->export_support = 1;
+ fc->flags.export_support = 1;
}
if (flags & FUSE_BIG_WRITES)
- fc->big_writes = 1;
+ fc->flags.big_writes = 1;
if (flags & FUSE_DONT_MASK)
fc->dont_mask = 1;
if (flags & FUSE_AUTO_INVAL_DATA)
- fc->auto_inval_data = 1;
+ fc->flags.auto_inval_data = 1;
else if (flags & FUSE_EXPLICIT_INVAL_DATA)
- fc->explicit_inval_data = 1;
+ fc->flags.explicit_inval_data = 1;
if (flags & FUSE_DO_READDIRPLUS) {
- fc->do_readdirplus = 1;
+ fc->flags.do_readdirplus = 1;
if (flags & FUSE_READDIRPLUS_AUTO)
- fc->readdirplus_auto = 1;
+ fc->flags.readdirplus_auto = 1;
}
if (flags & FUSE_ASYNC_DIO)
- fc->async_dio = 1;
+ fc->flags.async_dio = 1;
if (flags & FUSE_WRITEBACK_CACHE)
- fc->writeback_cache = 1;
+ fc->flags.writeback_cache = 1;
if (flags & FUSE_PARALLEL_DIROPS)
- fc->parallel_dirops = 1;
+ fc->flags.parallel_dirops = 1;
if (flags & FUSE_HANDLE_KILLPRIV)
- fc->handle_killpriv = 1;
+ fc->flags.handle_killpriv = 1;
if (arg->time_gran && arg->time_gran <= 1000000000)
fm->sb->s_time_gran = arg->time_gran;
if ((flags & FUSE_POSIX_ACL)) {
@@ -1173,7 +1173,7 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
fm->sb->s_xattr = fuse_acl_xattr_handlers;
}
if (flags & FUSE_CACHE_SYMLINKS)
- fc->cache_symlinks = 1;
+ fc->flags.cache_symlinks = 1;
if (flags & FUSE_ABORT_ERROR)
fc->abort_err = 1;
if (flags & FUSE_MAX_PAGES) {
@@ -1194,15 +1194,15 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
fm->sb->s_flags |= SB_NOSEC;
}
if (flags & FUSE_SETXATTR_EXT)
- fc->setxattr_ext = 1;
+ fc->flags.setxattr_ext = 1;
if (flags & FUSE_SECURITY_CTX)
fc->init_security = 1;
if (flags & FUSE_CREATE_SUPP_GROUP)
fc->create_supp_group = 1;
} else {
ra_pages = fc->max_read / PAGE_SIZE;
- fc->no_lock = 1;
- fc->no_flock = 1;
+ fc->flags.no_lock = 1;
+ fc->flags.no_flock = 1;
}

fm->sb->s_bdi->ra_pages =
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
index dc603479b30e..2a5bfb52ebf3 100644
--- a/fs/fuse/readdir.c
+++ b/fs/fuse/readdir.c
@@ -18,9 +18,9 @@ static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_inode *fi = get_fuse_inode(dir);

- if (!fc->do_readdirplus)
+ if (!fc->flags.do_readdirplus)
return false;
- if (!fc->readdirplus_auto)
+ if (!fc->flags.readdirplus_auto)
return true;
if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state))
return true;
@@ -246,7 +246,7 @@ static int fuse_direntplus_link(struct file *file,
if (IS_ERR(dentry))
return PTR_ERR(dentry);
}
- if (fc->readdirplus_auto)
+ if (fc->flags.readdirplus_auto)
set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
fuse_change_entry_timeout(dentry, o);

@@ -455,7 +455,7 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
* We're just about to start reading into the cache or reading the
* cache; both cases require an up-to-date mtime value.
*/
- if (!ctx->pos && fc->auto_inval_data) {
+ if (!ctx->pos && fc->flags.auto_inval_data) {
int err = fuse_update_attributes(inode, file, STATX_MTIME);

if (err)
diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c
index 0d3e7177fce0..13245c11ce25 100644
--- a/fs/fuse/xattr.c
+++ b/fs/fuse/xattr.c
@@ -19,7 +19,7 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value,
struct fuse_setxattr_in inarg;
int err;

- if (fm->fc->no_setxattr)
+ if (fm->fc->flags.no_setxattr)
return -EOPNOTSUPP;

memset(&inarg, 0, sizeof(inarg));
@@ -30,7 +30,7 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value,
args.opcode = FUSE_SETXATTR;
args.nodeid = get_node_id(inode);
args.in_numargs = 3;
- args.in_args[0].size = fm->fc->setxattr_ext ?
+ args.in_args[0].size = fm->fc->flags.setxattr_ext ?
sizeof(inarg) : FUSE_COMPAT_SETXATTR_IN_SIZE;
args.in_args[0].value = &inarg;
args.in_args[1].size = strlen(name) + 1;
@@ -39,7 +39,7 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value,
args.in_args[2].value = value;
err = fuse_simple_request(fm, &args);
if (err == -ENOSYS) {
- fm->fc->no_setxattr = 1;
+ fm->fc->flags.no_setxattr = 1;
err = -EOPNOTSUPP;
}
if (!err)
@@ -57,7 +57,7 @@ ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
struct fuse_getxattr_out outarg;
ssize_t ret;

- if (fm->fc->no_getxattr)
+ if (fm->fc->flags.no_getxattr)
return -EOPNOTSUPP;

memset(&inarg, 0, sizeof(inarg));
@@ -83,7 +83,7 @@ ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
if (!ret && !size)
ret = min_t(ssize_t, outarg.size, XATTR_SIZE_MAX);
if (ret == -ENOSYS) {
- fm->fc->no_getxattr = 1;
+ fm->fc->flags.no_getxattr = 1;
ret = -EOPNOTSUPP;
}
return ret;
@@ -121,7 +121,7 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
if (!fuse_allow_current_process(fm->fc))
return -EACCES;

- if (fm->fc->no_listxattr)
+ if (fm->fc->flags.no_listxattr)
return -EOPNOTSUPP;

memset(&inarg, 0, sizeof(inarg));
@@ -147,7 +147,7 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
if (ret > 0 && size)
ret = fuse_verify_xattr_list(list, ret);
if (ret == -ENOSYS) {
- fm->fc->no_listxattr = 1;
+ fm->fc->flags.no_listxattr = 1;
ret = -EOPNOTSUPP;
}
return ret;
@@ -159,7 +159,7 @@ int fuse_removexattr(struct inode *inode, const char *name)
FUSE_ARGS(args);
int err;

- if (fm->fc->no_removexattr)
+ if (fm->fc->flags.no_removexattr)
return -EOPNOTSUPP;

args.opcode = FUSE_REMOVEXATTR;
@@ -169,7 +169,7 @@ int fuse_removexattr(struct inode *inode, const char *name)
args.in_args[0].value = name;
err = fuse_simple_request(fm, &args);
if (err == -ENOSYS) {
- fm->fc->no_removexattr = 1;
+ fm->fc->flags.no_removexattr = 1;
err = -EOPNOTSUPP;
}
if (!err)
--
2.34.1