Commit 043930b1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fuse-update-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse

Pull fuse update from Miklos Szeredi:

 - Allow some write requests to proceed in parallel

 - Fix a performance problem with allow_sys_admin_access

 - Add a special kind of invalidation that doesn't immediately purge
   submounts

 - On revalidation treat the target of rename(RENAME_NOREPLACE) the same
   as open(O_EXCL)

 - Use type safe helpers for some mnt_userns transformations

* tag 'fuse-update-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: Rearrange fuse_allow_current_process checks
  fuse: allow non-extending parallel direct writes on the same file
  fuse: remove the unneeded result variable
  fuse: port to vfs{g,u}id_t and associated helpers
  fuse: Remove user_ns check for FUSE_DEV_IOC_CLONE
  fuse: always revalidate rename target dentry
  fuse: add "expire only" mode to FUSE_NOTIFY_INVAL_ENTRY
  fs/fuse: Replace kmap() with kmap_local_page()
parents 6df7cc22 b1387777
...@@ -545,7 +545,6 @@ static int cuse_channel_release(struct inode *inode, struct file *file) ...@@ -545,7 +545,6 @@ static int cuse_channel_release(struct inode *inode, struct file *file)
{ {
struct fuse_dev *fud = file->private_data; struct fuse_dev *fud = file->private_data;
struct cuse_conn *cc = fc_to_cc(fud->fc); struct cuse_conn *cc = fc_to_cc(fud->fc);
int rc;
/* remove from the conntbl, no more access from this point on */ /* remove from the conntbl, no more access from this point on */
mutex_lock(&cuse_lock); mutex_lock(&cuse_lock);
...@@ -560,9 +559,7 @@ static int cuse_channel_release(struct inode *inode, struct file *file) ...@@ -560,9 +559,7 @@ static int cuse_channel_release(struct inode *inode, struct file *file)
cdev_del(cc->cdev); cdev_del(cc->cdev);
} }
rc = fuse_dev_release(inode, file); /* puts the base reference */ return fuse_dev_release(inode, file);
return rc;
} }
static struct file_operations cuse_channel_fops; /* initialized during init */ static struct file_operations cuse_channel_fops; /* initialized during init */
......
...@@ -1498,7 +1498,7 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, ...@@ -1498,7 +1498,7 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
buf[outarg.namelen] = 0; buf[outarg.namelen] = 0;
down_read(&fc->killsb); down_read(&fc->killsb);
err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name); err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name, outarg.flags);
up_read(&fc->killsb); up_read(&fc->killsb);
kfree(buf); kfree(buf);
return err; return err;
...@@ -1546,7 +1546,7 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, ...@@ -1546,7 +1546,7 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
buf[outarg.namelen] = 0; buf[outarg.namelen] = 0;
down_read(&fc->killsb); down_read(&fc->killsb);
err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name); err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name, 0);
up_read(&fc->killsb); up_read(&fc->killsb);
kfree(buf); kfree(buf);
return err; return err;
...@@ -2267,8 +2267,7 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd, ...@@ -2267,8 +2267,7 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
* Check against file->f_op because CUSE * Check against file->f_op because CUSE
* uses the same ioctl handler. * uses the same ioctl handler.
*/ */
if (old->f_op == file->f_op && if (old->f_op == file->f_op)
old->f_cred->user_ns == file->f_cred->user_ns)
fud = fuse_get_dev(old); fud = fuse_get_dev(old);
if (fud) { if (fud) {
......
...@@ -214,7 +214,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) ...@@ -214,7 +214,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
if (inode && fuse_is_bad(inode)) if (inode && fuse_is_bad(inode))
goto invalid; goto invalid;
else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) || else if (time_before64(fuse_dentry_time(entry), get_jiffies_64()) ||
(flags & (LOOKUP_EXCL | LOOKUP_REVAL))) { (flags & (LOOKUP_EXCL | LOOKUP_REVAL | LOOKUP_RENAME_TARGET))) {
struct fuse_entry_out outarg; struct fuse_entry_out outarg;
FUSE_ARGS(args); FUSE_ARGS(args);
struct fuse_forget_link *forget; struct fuse_forget_link *forget;
...@@ -1170,7 +1170,7 @@ int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask) ...@@ -1170,7 +1170,7 @@ int fuse_update_attributes(struct inode *inode, struct file *file, u32 mask)
} }
int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
u64 child_nodeid, struct qstr *name) u64 child_nodeid, struct qstr *name, u32 flags)
{ {
int err = -ENOTDIR; int err = -ENOTDIR;
struct inode *parent; struct inode *parent;
...@@ -1197,7 +1197,9 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, ...@@ -1197,7 +1197,9 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
goto unlock; goto unlock;
fuse_dir_changed(parent); fuse_dir_changed(parent);
fuse_invalidate_entry(entry); if (!(flags & FUSE_EXPIRE_ONLY))
d_invalidate(entry);
fuse_invalidate_entry_cache(entry);
if (child_nodeid != 0 && d_really_is_positive(entry)) { if (child_nodeid != 0 && d_really_is_positive(entry)) {
inode_lock(d_inode(entry)); inode_lock(d_inode(entry));
...@@ -1235,6 +1237,18 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, ...@@ -1235,6 +1237,18 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
return err; return err;
} }
static inline bool fuse_permissible_uidgid(struct fuse_conn *fc)
{
const struct cred *cred = current_cred();
return (uid_eq(cred->euid, fc->user_id) &&
uid_eq(cred->suid, fc->user_id) &&
uid_eq(cred->uid, fc->user_id) &&
gid_eq(cred->egid, fc->group_id) &&
gid_eq(cred->sgid, fc->group_id) &&
gid_eq(cred->gid, fc->group_id));
}
/* /*
* Calling into a user-controlled filesystem gives the filesystem * Calling into a user-controlled filesystem gives the filesystem
* daemon ptrace-like capabilities over the current process. This * daemon ptrace-like capabilities over the current process. This
...@@ -1248,26 +1262,19 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, ...@@ -1248,26 +1262,19 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
* for which the owner of the mount has ptrace privilege. This * for which the owner of the mount has ptrace privilege. This
* excludes processes started by other users, suid or sgid processes. * excludes processes started by other users, suid or sgid processes.
*/ */
int fuse_allow_current_process(struct fuse_conn *fc) bool fuse_allow_current_process(struct fuse_conn *fc)
{ {
const struct cred *cred; bool allow;
if (allow_sys_admin_access && capable(CAP_SYS_ADMIN))
return 1;
if (fc->allow_other) if (fc->allow_other)
return current_in_userns(fc->user_ns); allow = current_in_userns(fc->user_ns);
else
allow = fuse_permissible_uidgid(fc);
cred = current_cred(); if (!allow && allow_sys_admin_access && capable(CAP_SYS_ADMIN))
if (uid_eq(cred->euid, fc->user_id) && allow = true;
uid_eq(cred->suid, fc->user_id) &&
uid_eq(cred->uid, fc->user_id) &&
gid_eq(cred->egid, fc->group_id) &&
gid_eq(cred->sgid, fc->group_id) &&
gid_eq(cred->gid, fc->group_id))
return 1;
return 0; return allow;
} }
static int fuse_access(struct inode *inode, int mask) static int fuse_access(struct inode *inode, int mask)
......
...@@ -1563,14 +1563,47 @@ static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to) ...@@ -1563,14 +1563,47 @@ static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to)
return res; return res;
} }
static bool fuse_direct_write_extending_i_size(struct kiocb *iocb,
struct iov_iter *iter)
{
struct inode *inode = file_inode(iocb->ki_filp);
return iocb->ki_pos + iov_iter_count(iter) > i_size_read(inode);
}
static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
{ {
struct inode *inode = file_inode(iocb->ki_filp); struct inode *inode = file_inode(iocb->ki_filp);
struct file *file = iocb->ki_filp;
struct fuse_file *ff = file->private_data;
struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb);
ssize_t res; ssize_t res;
bool exclusive_lock =
!(ff->open_flags & FOPEN_PARALLEL_DIRECT_WRITES) ||
iocb->ki_flags & IOCB_APPEND ||
fuse_direct_write_extending_i_size(iocb, from);
/*
* Take exclusive lock if
* - Parallel direct writes are disabled - a user space decision
* - Parallel direct writes are enabled and i_size is being extended.
* This might not be needed at all, but needs further investigation.
*/
if (exclusive_lock)
inode_lock(inode);
else {
inode_lock_shared(inode);
/* A race with truncate might have come up as the decision for
* the lock type was done without holding the lock, check again.
*/
if (fuse_direct_write_extending_i_size(iocb, from)) {
inode_unlock_shared(inode);
inode_lock(inode);
exclusive_lock = true;
}
}
/* Don't allow parallel writes to the same file */
inode_lock(inode);
res = generic_write_checks(iocb, from); res = generic_write_checks(iocb, from);
if (res > 0) { if (res > 0) {
if (!is_sync_kiocb(iocb) && iocb->ki_flags & IOCB_DIRECT) { if (!is_sync_kiocb(iocb) && iocb->ki_flags & IOCB_DIRECT) {
...@@ -1581,7 +1614,10 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -1581,7 +1614,10 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
fuse_write_update_attr(inode, iocb->ki_pos, res); fuse_write_update_attr(inode, iocb->ki_pos, res);
} }
} }
inode_unlock(inode); if (exclusive_lock)
inode_unlock(inode);
else
inode_unlock_shared(inode);
return res; return res;
} }
...@@ -2931,6 +2967,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ...@@ -2931,6 +2967,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
if (iov_iter_rw(iter) == WRITE) { if (iov_iter_rw(iter) == WRITE) {
fuse_write_update_attr(inode, pos, ret); fuse_write_update_attr(inode, pos, ret);
/* For extending writes we already hold exclusive lock */
if (ret < 0 && offset + count > i_size) if (ret < 0 && offset + count > i_size)
fuse_do_truncate(file); fuse_do_truncate(file);
} }
......
...@@ -1179,7 +1179,7 @@ bool fuse_invalid_attr(struct fuse_attr *attr); ...@@ -1179,7 +1179,7 @@ bool fuse_invalid_attr(struct fuse_attr *attr);
/** /**
* Is current process allowed to perform filesystem operation? * Is current process allowed to perform filesystem operation?
*/ */
int fuse_allow_current_process(struct fuse_conn *fc); bool fuse_allow_current_process(struct fuse_conn *fc);
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id); u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
...@@ -1220,7 +1220,7 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, ...@@ -1220,7 +1220,7 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid,
* then the dentry is unhashed (d_delete()). * then the dentry is unhashed (d_delete()).
*/ */
int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
u64 child_nodeid, struct qstr *name); u64 child_nodeid, struct qstr *name, u32 flags);
int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file, int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file,
bool isdir); bool isdir);
......
...@@ -547,9 +547,9 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx) ...@@ -547,9 +547,9 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
* Contents of the page are now protected against changing by holding * Contents of the page are now protected against changing by holding
* the page lock. * the page lock.
*/ */
addr = kmap(page); addr = kmap_local_page(page);
res = fuse_parse_cache(ff, addr, size, ctx); res = fuse_parse_cache(ff, addr, size, ctx);
kunmap(page); kunmap_local(addr);
unlock_page(page); unlock_page(page);
put_page(page); put_page(page);
......
...@@ -197,6 +197,10 @@ ...@@ -197,6 +197,10 @@
* *
* 7.37 * 7.37
* - add FUSE_TMPFILE * - add FUSE_TMPFILE
*
* 7.38
* - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry
* - add FOPEN_PARALLEL_DIRECT_WRITES
*/ */
#ifndef _LINUX_FUSE_H #ifndef _LINUX_FUSE_H
...@@ -232,7 +236,7 @@ ...@@ -232,7 +236,7 @@
#define FUSE_KERNEL_VERSION 7 #define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */ /** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 37 #define FUSE_KERNEL_MINOR_VERSION 38
/** The node ID of the root inode */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
...@@ -304,6 +308,7 @@ struct fuse_file_lock { ...@@ -304,6 +308,7 @@ struct fuse_file_lock {
* FOPEN_CACHE_DIR: allow caching this directory * FOPEN_CACHE_DIR: allow caching this directory
* FOPEN_STREAM: the file is stream-like (no file position at all) * FOPEN_STREAM: the file is stream-like (no file position at all)
* FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
* FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode
*/ */
#define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1) #define FOPEN_KEEP_CACHE (1 << 1)
...@@ -311,6 +316,7 @@ struct fuse_file_lock { ...@@ -311,6 +316,7 @@ struct fuse_file_lock {
#define FOPEN_CACHE_DIR (1 << 3) #define FOPEN_CACHE_DIR (1 << 3)
#define FOPEN_STREAM (1 << 4) #define FOPEN_STREAM (1 << 4)
#define FOPEN_NOFLUSH (1 << 5) #define FOPEN_NOFLUSH (1 << 5)
#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
/** /**
* INIT request/reply flags * INIT request/reply flags
...@@ -491,6 +497,12 @@ struct fuse_file_lock { ...@@ -491,6 +497,12 @@ struct fuse_file_lock {
*/ */
#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0) #define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
/**
* notify_inval_entry flags
* FUSE_EXPIRE_ONLY
*/
#define FUSE_EXPIRE_ONLY (1 << 0)
enum fuse_opcode { enum fuse_opcode {
FUSE_LOOKUP = 1, FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */ FUSE_FORGET = 2, /* no reply */
...@@ -919,7 +931,7 @@ struct fuse_notify_inval_inode_out { ...@@ -919,7 +931,7 @@ struct fuse_notify_inval_inode_out {
struct fuse_notify_inval_entry_out { struct fuse_notify_inval_entry_out {
uint64_t parent; uint64_t parent;
uint32_t namelen; uint32_t namelen;
uint32_t padding; uint32_t flags;
}; };
struct fuse_notify_delete_out { struct fuse_notify_delete_out {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment