Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: invalidation reverse calls fuse: allow umask processing in userspace fuse: fix bad return value in fuse_file_poll() fuse: fix return value of fuse_dev_write()
This commit is contained in:
commit
fa172f4006
6 changed files with 262 additions and 11 deletions
|
@ -849,6 +849,81 @@ static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int fuse_notify_inval_inode(struct fuse_conn *fc, unsigned int size,
|
||||
struct fuse_copy_state *cs)
|
||||
{
|
||||
struct fuse_notify_inval_inode_out outarg;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (size != sizeof(outarg))
|
||||
goto err;
|
||||
|
||||
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
||||
if (err)
|
||||
goto err;
|
||||
fuse_copy_finish(cs);
|
||||
|
||||
down_read(&fc->killsb);
|
||||
err = -ENOENT;
|
||||
if (!fc->sb)
|
||||
goto err_unlock;
|
||||
|
||||
err = fuse_reverse_inval_inode(fc->sb, outarg.ino,
|
||||
outarg.off, outarg.len);
|
||||
|
||||
err_unlock:
|
||||
up_read(&fc->killsb);
|
||||
return err;
|
||||
|
||||
err:
|
||||
fuse_copy_finish(cs);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
|
||||
struct fuse_copy_state *cs)
|
||||
{
|
||||
struct fuse_notify_inval_entry_out outarg;
|
||||
int err = -EINVAL;
|
||||
char buf[FUSE_NAME_MAX+1];
|
||||
struct qstr name;
|
||||
|
||||
if (size < sizeof(outarg))
|
||||
goto err;
|
||||
|
||||
err = fuse_copy_one(cs, &outarg, sizeof(outarg));
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
err = -ENAMETOOLONG;
|
||||
if (outarg.namelen > FUSE_NAME_MAX)
|
||||
goto err;
|
||||
|
||||
name.name = buf;
|
||||
name.len = outarg.namelen;
|
||||
err = fuse_copy_one(cs, buf, outarg.namelen + 1);
|
||||
if (err)
|
||||
goto err;
|
||||
fuse_copy_finish(cs);
|
||||
buf[outarg.namelen] = 0;
|
||||
name.hash = full_name_hash(name.name, name.len);
|
||||
|
||||
down_read(&fc->killsb);
|
||||
err = -ENOENT;
|
||||
if (!fc->sb)
|
||||
goto err_unlock;
|
||||
|
||||
err = fuse_reverse_inval_entry(fc->sb, outarg.parent, &name);
|
||||
|
||||
err_unlock:
|
||||
up_read(&fc->killsb);
|
||||
return err;
|
||||
|
||||
err:
|
||||
fuse_copy_finish(cs);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
|
||||
unsigned int size, struct fuse_copy_state *cs)
|
||||
{
|
||||
|
@ -856,6 +931,12 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
|
|||
case FUSE_NOTIFY_POLL:
|
||||
return fuse_notify_poll(fc, size, cs);
|
||||
|
||||
case FUSE_NOTIFY_INVAL_INODE:
|
||||
return fuse_notify_inval_inode(fc, size, cs);
|
||||
|
||||
case FUSE_NOTIFY_INVAL_ENTRY:
|
||||
return fuse_notify_inval_entry(fc, size, cs);
|
||||
|
||||
default:
|
||||
fuse_copy_finish(cs);
|
||||
return -EINVAL;
|
||||
|
@ -910,7 +991,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov,
|
|||
unsigned long nr_segs, loff_t pos)
|
||||
{
|
||||
int err;
|
||||
unsigned nbytes = iov_length(iov, nr_segs);
|
||||
size_t nbytes = iov_length(iov, nr_segs);
|
||||
struct fuse_req *req;
|
||||
struct fuse_out_header oh;
|
||||
struct fuse_copy_state cs;
|
||||
|
|
|
@ -375,7 +375,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
|
|||
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||
struct fuse_req *req;
|
||||
struct fuse_req *forget_req;
|
||||
struct fuse_open_in inarg;
|
||||
struct fuse_create_in inarg;
|
||||
struct fuse_open_out outopen;
|
||||
struct fuse_entry_out outentry;
|
||||
struct fuse_file *ff;
|
||||
|
@ -399,15 +399,20 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
|
|||
if (!ff)
|
||||
goto out_put_request;
|
||||
|
||||
if (!fc->dont_mask)
|
||||
mode &= ~current_umask();
|
||||
|
||||
flags &= ~O_NOCTTY;
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
memset(&outentry, 0, sizeof(outentry));
|
||||
inarg.flags = flags;
|
||||
inarg.mode = mode;
|
||||
inarg.umask = current_umask();
|
||||
req->in.h.opcode = FUSE_CREATE;
|
||||
req->in.h.nodeid = get_node_id(dir);
|
||||
req->in.numargs = 2;
|
||||
req->in.args[0].size = sizeof(inarg);
|
||||
req->in.args[0].size = fc->minor < 12 ? sizeof(struct fuse_open_in) :
|
||||
sizeof(inarg);
|
||||
req->in.args[0].value = &inarg;
|
||||
req->in.args[1].size = entry->d_name.len + 1;
|
||||
req->in.args[1].value = entry->d_name.name;
|
||||
|
@ -546,12 +551,17 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
|
|||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
if (!fc->dont_mask)
|
||||
mode &= ~current_umask();
|
||||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.mode = mode;
|
||||
inarg.rdev = new_encode_dev(rdev);
|
||||
inarg.umask = current_umask();
|
||||
req->in.h.opcode = FUSE_MKNOD;
|
||||
req->in.numargs = 2;
|
||||
req->in.args[0].size = sizeof(inarg);
|
||||
req->in.args[0].size = fc->minor < 12 ? FUSE_COMPAT_MKNOD_IN_SIZE :
|
||||
sizeof(inarg);
|
||||
req->in.args[0].value = &inarg;
|
||||
req->in.args[1].size = entry->d_name.len + 1;
|
||||
req->in.args[1].value = entry->d_name.name;
|
||||
|
@ -578,8 +588,12 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
|
|||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
if (!fc->dont_mask)
|
||||
mode &= ~current_umask();
|
||||
|
||||
memset(&inarg, 0, sizeof(inarg));
|
||||
inarg.mode = mode;
|
||||
inarg.umask = current_umask();
|
||||
req->in.h.opcode = FUSE_MKDIR;
|
||||
req->in.numargs = 2;
|
||||
req->in.args[0].size = sizeof(inarg);
|
||||
|
@ -845,6 +859,43 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
|
|||
return err;
|
||||
}
|
||||
|
||||
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
|
||||
struct qstr *name)
|
||||
{
|
||||
int err = -ENOTDIR;
|
||||
struct inode *parent;
|
||||
struct dentry *dir;
|
||||
struct dentry *entry;
|
||||
|
||||
parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid);
|
||||
if (!parent)
|
||||
return -ENOENT;
|
||||
|
||||
mutex_lock(&parent->i_mutex);
|
||||
if (!S_ISDIR(parent->i_mode))
|
||||
goto unlock;
|
||||
|
||||
err = -ENOENT;
|
||||
dir = d_find_alias(parent);
|
||||
if (!dir)
|
||||
goto unlock;
|
||||
|
||||
entry = d_lookup(dir, name);
|
||||
dput(dir);
|
||||
if (!entry)
|
||||
goto unlock;
|
||||
|
||||
fuse_invalidate_attr(parent);
|
||||
fuse_invalidate_entry(entry);
|
||||
dput(entry);
|
||||
err = 0;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&parent->i_mutex);
|
||||
iput(parent);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calling into a user-controlled filesystem gives the filesystem
|
||||
* daemon ptrace-like capabilities over the requester process. This
|
||||
|
|
|
@ -1922,7 +1922,7 @@ unsigned fuse_file_poll(struct file *file, poll_table *wait)
|
|||
|
||||
req = fuse_get_req(fc);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
return POLLERR;
|
||||
|
||||
req->in.h.opcode = FUSE_POLL;
|
||||
req->in.h.nodeid = ff->nodeid;
|
||||
|
|
|
@ -446,6 +446,9 @@ struct fuse_conn {
|
|||
/** Do multi-page cached writes */
|
||||
unsigned big_writes:1;
|
||||
|
||||
/** Don't apply umask to creation modes */
|
||||
unsigned dont_mask:1;
|
||||
|
||||
/** The number of requests waiting for completion */
|
||||
atomic_t num_waiting;
|
||||
|
||||
|
@ -481,6 +484,12 @@ struct fuse_conn {
|
|||
|
||||
/** Called on final put */
|
||||
void (*release)(struct fuse_conn *);
|
||||
|
||||
/** Super block for this connection. */
|
||||
struct super_block *sb;
|
||||
|
||||
/** Read/write semaphore to hold when accessing sb. */
|
||||
struct rw_semaphore killsb;
|
||||
};
|
||||
|
||||
static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
|
||||
|
@ -508,6 +517,11 @@ extern const struct file_operations fuse_dev_operations;
|
|||
|
||||
extern const struct dentry_operations fuse_dentry_operations;
|
||||
|
||||
/**
|
||||
* Inode to nodeid comparison.
|
||||
*/
|
||||
int fuse_inode_eq(struct inode *inode, void *_nodeidp);
|
||||
|
||||
/**
|
||||
* Get a filled in inode
|
||||
*/
|
||||
|
@ -708,6 +722,19 @@ void fuse_release_nowrite(struct inode *inode);
|
|||
|
||||
u64 fuse_get_attr_version(struct fuse_conn *fc);
|
||||
|
||||
/**
|
||||
* File-system tells the kernel to invalidate cache for the given node id.
|
||||
*/
|
||||
int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
|
||||
loff_t offset, loff_t len);
|
||||
|
||||
/**
|
||||
* File-system tells the kernel to invalidate parent attributes and
|
||||
* the dentry matching parent/name.
|
||||
*/
|
||||
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
|
||||
struct qstr *name);
|
||||
|
||||
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
|
||||
bool isdir);
|
||||
ssize_t fuse_direct_io(struct file *file, const char __user *buf,
|
||||
|
|
|
@ -206,7 +206,7 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
|
|||
BUG();
|
||||
}
|
||||
|
||||
static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
|
||||
int fuse_inode_eq(struct inode *inode, void *_nodeidp)
|
||||
{
|
||||
u64 nodeid = *(u64 *) _nodeidp;
|
||||
if (get_node_id(inode) == nodeid)
|
||||
|
@ -257,6 +257,31 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
|
|||
return inode;
|
||||
}
|
||||
|
||||
int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
|
||||
loff_t offset, loff_t len)
|
||||
{
|
||||
struct inode *inode;
|
||||
pgoff_t pg_start;
|
||||
pgoff_t pg_end;
|
||||
|
||||
inode = ilookup5(sb, nodeid, fuse_inode_eq, &nodeid);
|
||||
if (!inode)
|
||||
return -ENOENT;
|
||||
|
||||
fuse_invalidate_attr(inode);
|
||||
if (offset >= 0) {
|
||||
pg_start = offset >> PAGE_CACHE_SHIFT;
|
||||
if (len <= 0)
|
||||
pg_end = -1;
|
||||
else
|
||||
pg_end = (offset + len - 1) >> PAGE_CACHE_SHIFT;
|
||||
invalidate_inode_pages2_range(inode->i_mapping,
|
||||
pg_start, pg_end);
|
||||
}
|
||||
iput(inode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fuse_umount_begin(struct super_block *sb)
|
||||
{
|
||||
fuse_abort_conn(get_fuse_conn_super(sb));
|
||||
|
@ -480,6 +505,7 @@ void fuse_conn_init(struct fuse_conn *fc)
|
|||
memset(fc, 0, sizeof(*fc));
|
||||
spin_lock_init(&fc->lock);
|
||||
mutex_init(&fc->inst_mutex);
|
||||
init_rwsem(&fc->killsb);
|
||||
atomic_set(&fc->count, 1);
|
||||
init_waitqueue_head(&fc->waitq);
|
||||
init_waitqueue_head(&fc->blocked_waitq);
|
||||
|
@ -725,6 +751,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
|
|||
}
|
||||
if (arg->flags & FUSE_BIG_WRITES)
|
||||
fc->big_writes = 1;
|
||||
if (arg->flags & FUSE_DONT_MASK)
|
||||
fc->dont_mask = 1;
|
||||
} else {
|
||||
ra_pages = fc->max_read / PAGE_CACHE_SIZE;
|
||||
fc->no_lock = 1;
|
||||
|
@ -748,7 +776,7 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
|
|||
arg->minor = FUSE_KERNEL_MINOR_VERSION;
|
||||
arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE;
|
||||
arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
|
||||
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES;
|
||||
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK;
|
||||
req->in.h.opcode = FUSE_INIT;
|
||||
req->in.numargs = 1;
|
||||
req->in.args[0].size = sizeof(*arg);
|
||||
|
@ -860,10 +888,16 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
|||
fuse_conn_init(fc);
|
||||
|
||||
fc->dev = sb->s_dev;
|
||||
fc->sb = sb;
|
||||
err = fuse_bdi_init(fc, sb);
|
||||
if (err)
|
||||
goto err_put_conn;
|
||||
|
||||
/* Handle umasking inside the fuse code */
|
||||
if (sb->s_flags & MS_POSIXACL)
|
||||
fc->dont_mask = 1;
|
||||
sb->s_flags |= MS_POSIXACL;
|
||||
|
||||
fc->release = fuse_free_conn;
|
||||
fc->flags = d.flags;
|
||||
fc->user_id = d.user_id;
|
||||
|
@ -941,12 +975,25 @@ static int fuse_get_sb(struct file_system_type *fs_type,
|
|||
return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super, mnt);
|
||||
}
|
||||
|
||||
static void fuse_kill_sb_anon(struct super_block *sb)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn_super(sb);
|
||||
|
||||
if (fc) {
|
||||
down_write(&fc->killsb);
|
||||
fc->sb = NULL;
|
||||
up_write(&fc->killsb);
|
||||
}
|
||||
|
||||
kill_anon_super(sb);
|
||||
}
|
||||
|
||||
static struct file_system_type fuse_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "fuse",
|
||||
.fs_flags = FS_HAS_SUBTYPE,
|
||||
.get_sb = fuse_get_sb,
|
||||
.kill_sb = kill_anon_super,
|
||||
.kill_sb = fuse_kill_sb_anon,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_BLOCK
|
||||
|
@ -958,11 +1005,24 @@ static int fuse_get_sb_blk(struct file_system_type *fs_type,
|
|||
mnt);
|
||||
}
|
||||
|
||||
static void fuse_kill_sb_blk(struct super_block *sb)
|
||||
{
|
||||
struct fuse_conn *fc = get_fuse_conn_super(sb);
|
||||
|
||||
if (fc) {
|
||||
down_write(&fc->killsb);
|
||||
fc->sb = NULL;
|
||||
up_write(&fc->killsb);
|
||||
}
|
||||
|
||||
kill_block_super(sb);
|
||||
}
|
||||
|
||||
static struct file_system_type fuseblk_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "fuseblk",
|
||||
.get_sb = fuse_get_sb_blk,
|
||||
.kill_sb = kill_block_super,
|
||||
.kill_sb = fuse_kill_sb_blk,
|
||||
.fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE,
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,11 @@
|
|||
* - add IOCTL message
|
||||
* - add unsolicited notification support
|
||||
* - add POLL message and NOTIFY_POLL notification
|
||||
*
|
||||
* 7.12
|
||||
* - add umask flag to input argument of open, mknod and mkdir
|
||||
* - add notification messages for invalidation of inodes and
|
||||
* directory entries
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
|
@ -36,7 +41,7 @@
|
|||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 11
|
||||
#define FUSE_KERNEL_MINOR_VERSION 12
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
|
@ -112,6 +117,7 @@ struct fuse_file_lock {
|
|||
* INIT request/reply flags
|
||||
*
|
||||
* FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
|
||||
* FUSE_DONT_MASK: don't apply umask to file mode on create operations
|
||||
*/
|
||||
#define FUSE_ASYNC_READ (1 << 0)
|
||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||
|
@ -119,6 +125,7 @@ struct fuse_file_lock {
|
|||
#define FUSE_ATOMIC_O_TRUNC (1 << 3)
|
||||
#define FUSE_EXPORT_SUPPORT (1 << 4)
|
||||
#define FUSE_BIG_WRITES (1 << 5)
|
||||
#define FUSE_DONT_MASK (1 << 6)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
|
@ -224,6 +231,8 @@ enum fuse_opcode {
|
|||
|
||||
enum fuse_notify_code {
|
||||
FUSE_NOTIFY_POLL = 1,
|
||||
FUSE_NOTIFY_INVAL_INODE = 2,
|
||||
FUSE_NOTIFY_INVAL_ENTRY = 3,
|
||||
FUSE_NOTIFY_CODE_MAX,
|
||||
};
|
||||
|
||||
|
@ -262,14 +271,18 @@ struct fuse_attr_out {
|
|||
struct fuse_attr attr;
|
||||
};
|
||||
|
||||
#define FUSE_COMPAT_MKNOD_IN_SIZE 8
|
||||
|
||||
struct fuse_mknod_in {
|
||||
__u32 mode;
|
||||
__u32 rdev;
|
||||
__u32 umask;
|
||||
__u32 padding;
|
||||
};
|
||||
|
||||
struct fuse_mkdir_in {
|
||||
__u32 mode;
|
||||
__u32 padding;
|
||||
__u32 umask;
|
||||
};
|
||||
|
||||
struct fuse_rename_in {
|
||||
|
@ -300,8 +313,15 @@ struct fuse_setattr_in {
|
|||
};
|
||||
|
||||
struct fuse_open_in {
|
||||
__u32 flags;
|
||||
__u32 unused;
|
||||
};
|
||||
|
||||
struct fuse_create_in {
|
||||
__u32 flags;
|
||||
__u32 mode;
|
||||
__u32 umask;
|
||||
__u32 padding;
|
||||
};
|
||||
|
||||
struct fuse_open_out {
|
||||
|
@ -508,4 +528,16 @@ struct fuse_dirent {
|
|||
#define FUSE_DIRENT_SIZE(d) \
|
||||
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
|
||||
|
||||
struct fuse_notify_inval_inode_out {
|
||||
__u64 ino;
|
||||
__s64 off;
|
||||
__s64 len;
|
||||
};
|
||||
|
||||
struct fuse_notify_inval_entry_out {
|
||||
__u64 parent;
|
||||
__u32 namelen;
|
||||
__u32 padding;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_FUSE_H */
|
||||
|
|
Loading…
Reference in a new issue