Merge tag 'fuse-update-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
Pull fuse updates from Miklos Szeredi: - Improve performance of virtio-fs in mixed read/write workloads - Try to revalidate cache before returning EEXIST on exclusive create - Add a couple of miscellaneous bug fixes as well as some code cleanups * tag 'fuse-update-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: fix bad inode fuse: support SB_NOSEC flag to improve write performance fuse: add a flag FUSE_OPEN_KILL_SUIDGID for open() request fuse: don't send ATTR_MODE to kill suid/sgid for handle_killpriv_v2 fuse: setattr should set FATTR_KILL_SUIDGID fuse: set FUSE_WRITE_KILL_SUIDGID in cached write path fuse: rename FUSE_WRITE_KILL_PRIV to FUSE_WRITE_KILL_SUIDGID fuse: introduce the notion of FUSE_HANDLE_KILLPRIV_V2 fuse: always revalidate if exclusive create virtiofs: clean up error handling in virtio_fs_get_tree() fuse: add fuse_sb_destroy() helper fuse: simplify get_fuse_conn*() fuse: get rid of fuse_mount refcount virtiofs: simplify sb setup virtiofs fix leak in setup fuse: launder page should wait for page writeback
This commit is contained in:
@@ -19,6 +19,9 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type)
|
|||||||
void *value = NULL;
|
void *value = NULL;
|
||||||
struct posix_acl *acl;
|
struct posix_acl *acl;
|
||||||
|
|
||||||
|
if (fuse_is_bad(inode))
|
||||||
|
return ERR_PTR(-EIO);
|
||||||
|
|
||||||
if (!fc->posix_acl || fc->no_getxattr)
|
if (!fc->posix_acl || fc->no_getxattr)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -53,6 +56,9 @@ int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type)
|
|||||||
const char *name;
|
const char *name;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (fuse_is_bad(inode))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
if (!fc->posix_acl || fc->no_setxattr)
|
if (!fc->posix_acl || fc->no_setxattr)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
|||||||
@@ -202,10 +202,10 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
inode = d_inode_rcu(entry);
|
inode = d_inode_rcu(entry);
|
||||||
if (inode && is_bad_inode(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_REVAL)) {
|
(flags & (LOOKUP_EXCL | LOOKUP_REVAL))) {
|
||||||
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;
|
||||||
@@ -328,12 +328,11 @@ static struct vfsmount *fuse_dentry_automount(struct path *path)
|
|||||||
if (!fm)
|
if (!fm)
|
||||||
goto out_put_fsc;
|
goto out_put_fsc;
|
||||||
|
|
||||||
refcount_set(&fm->count, 1);
|
|
||||||
fsc->s_fs_info = fm;
|
fsc->s_fs_info = fm;
|
||||||
sb = sget_fc(fsc, NULL, set_anon_super_fc);
|
sb = sget_fc(fsc, NULL, set_anon_super_fc);
|
||||||
if (IS_ERR(sb)) {
|
if (IS_ERR(sb)) {
|
||||||
err = PTR_ERR(sb);
|
err = PTR_ERR(sb);
|
||||||
fuse_mount_put(fm);
|
kfree(fm);
|
||||||
goto out_put_fsc;
|
goto out_put_fsc;
|
||||||
}
|
}
|
||||||
fm->fc = fuse_conn_get(fc);
|
fm->fc = fuse_conn_get(fc);
|
||||||
@@ -463,6 +462,9 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
|
|||||||
bool outarg_valid = true;
|
bool outarg_valid = true;
|
||||||
bool locked;
|
bool locked;
|
||||||
|
|
||||||
|
if (fuse_is_bad(dir))
|
||||||
|
return ERR_PTR(-EIO);
|
||||||
|
|
||||||
locked = fuse_lock_inode(dir);
|
locked = fuse_lock_inode(dir);
|
||||||
err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name,
|
err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name,
|
||||||
&outarg, &inode);
|
&outarg, &inode);
|
||||||
@@ -542,6 +544,12 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
|||||||
inarg.flags = flags;
|
inarg.flags = flags;
|
||||||
inarg.mode = mode;
|
inarg.mode = mode;
|
||||||
inarg.umask = current_umask();
|
inarg.umask = current_umask();
|
||||||
|
|
||||||
|
if (fm->fc->handle_killpriv_v2 && (flags & O_TRUNC) &&
|
||||||
|
!(flags & O_EXCL) && !capable(CAP_FSETID)) {
|
||||||
|
inarg.open_flags |= FUSE_OPEN_KILL_SUIDGID;
|
||||||
|
}
|
||||||
|
|
||||||
args.opcode = FUSE_CREATE;
|
args.opcode = FUSE_CREATE;
|
||||||
args.nodeid = get_node_id(dir);
|
args.nodeid = get_node_id(dir);
|
||||||
args.in_numargs = 2;
|
args.in_numargs = 2;
|
||||||
@@ -606,6 +614,9 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
|
|||||||
struct fuse_conn *fc = get_fuse_conn(dir);
|
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||||
struct dentry *res = NULL;
|
struct dentry *res = NULL;
|
||||||
|
|
||||||
|
if (fuse_is_bad(dir))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
if (d_in_lookup(entry)) {
|
if (d_in_lookup(entry)) {
|
||||||
res = fuse_lookup(dir, entry, 0);
|
res = fuse_lookup(dir, entry, 0);
|
||||||
if (IS_ERR(res))
|
if (IS_ERR(res))
|
||||||
@@ -654,6 +665,9 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
|
|||||||
int err;
|
int err;
|
||||||
struct fuse_forget_link *forget;
|
struct fuse_forget_link *forget;
|
||||||
|
|
||||||
|
if (fuse_is_bad(dir))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
forget = fuse_alloc_forget();
|
forget = fuse_alloc_forget();
|
||||||
if (!forget)
|
if (!forget)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@@ -781,6 +795,9 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
|||||||
struct fuse_mount *fm = get_fuse_mount(dir);
|
struct fuse_mount *fm = get_fuse_mount(dir);
|
||||||
FUSE_ARGS(args);
|
FUSE_ARGS(args);
|
||||||
|
|
||||||
|
if (fuse_is_bad(dir))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
args.opcode = FUSE_UNLINK;
|
args.opcode = FUSE_UNLINK;
|
||||||
args.nodeid = get_node_id(dir);
|
args.nodeid = get_node_id(dir);
|
||||||
args.in_numargs = 1;
|
args.in_numargs = 1;
|
||||||
@@ -817,6 +834,9 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
|
|||||||
struct fuse_mount *fm = get_fuse_mount(dir);
|
struct fuse_mount *fm = get_fuse_mount(dir);
|
||||||
FUSE_ARGS(args);
|
FUSE_ARGS(args);
|
||||||
|
|
||||||
|
if (fuse_is_bad(dir))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
args.opcode = FUSE_RMDIR;
|
args.opcode = FUSE_RMDIR;
|
||||||
args.nodeid = get_node_id(dir);
|
args.nodeid = get_node_id(dir);
|
||||||
args.in_numargs = 1;
|
args.in_numargs = 1;
|
||||||
@@ -895,6 +915,9 @@ static int fuse_rename2(struct inode *olddir, struct dentry *oldent,
|
|||||||
struct fuse_conn *fc = get_fuse_conn(olddir);
|
struct fuse_conn *fc = get_fuse_conn(olddir);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
if (fuse_is_bad(olddir))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
|
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@@ -1030,7 +1053,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
|
|||||||
if (!err) {
|
if (!err) {
|
||||||
if (fuse_invalid_attr(&outarg.attr) ||
|
if (fuse_invalid_attr(&outarg.attr) ||
|
||||||
(inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
(inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
||||||
make_bad_inode(inode);
|
fuse_make_bad(inode);
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
} else {
|
} else {
|
||||||
fuse_change_attributes(inode, &outarg.attr,
|
fuse_change_attributes(inode, &outarg.attr,
|
||||||
@@ -1232,6 +1255,9 @@ static int fuse_permission(struct inode *inode, int mask)
|
|||||||
bool refreshed = false;
|
bool refreshed = false;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
if (fuse_is_bad(inode))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
if (!fuse_allow_current_process(fc))
|
if (!fuse_allow_current_process(fc))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
@@ -1327,7 +1353,7 @@ static const char *fuse_get_link(struct dentry *dentry, struct inode *inode,
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
if (fc->cache_symlinks)
|
if (fc->cache_symlinks)
|
||||||
@@ -1375,7 +1401,7 @@ static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end,
|
|||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if (fc->no_fsyncdir)
|
if (fc->no_fsyncdir)
|
||||||
@@ -1649,10 +1675,20 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
|||||||
inarg.valid |= FATTR_FH;
|
inarg.valid |= FATTR_FH;
|
||||||
inarg.fh = ff->fh;
|
inarg.fh = ff->fh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Kill suid/sgid for non-directory chown unconditionally */
|
||||||
|
if (fc->handle_killpriv_v2 && !S_ISDIR(inode->i_mode) &&
|
||||||
|
attr->ia_valid & (ATTR_UID | ATTR_GID))
|
||||||
|
inarg.valid |= FATTR_KILL_SUIDGID;
|
||||||
|
|
||||||
if (attr->ia_valid & ATTR_SIZE) {
|
if (attr->ia_valid & ATTR_SIZE) {
|
||||||
/* For mandatory locking in truncate */
|
/* For mandatory locking in truncate */
|
||||||
inarg.valid |= FATTR_LOCKOWNER;
|
inarg.valid |= FATTR_LOCKOWNER;
|
||||||
inarg.lock_owner = fuse_lock_owner_id(fc, current->files);
|
inarg.lock_owner = fuse_lock_owner_id(fc, current->files);
|
||||||
|
|
||||||
|
/* Kill suid/sgid for truncate only if no CAP_FSETID */
|
||||||
|
if (fc->handle_killpriv_v2 && !capable(CAP_FSETID))
|
||||||
|
inarg.valid |= FATTR_KILL_SUIDGID;
|
||||||
}
|
}
|
||||||
fuse_setattr_fill(fc, &args, inode, &inarg, &outarg);
|
fuse_setattr_fill(fc, &args, inode, &inarg, &outarg);
|
||||||
err = fuse_simple_request(fm, &args);
|
err = fuse_simple_request(fm, &args);
|
||||||
@@ -1664,7 +1700,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
|||||||
|
|
||||||
if (fuse_invalid_attr(&outarg.attr) ||
|
if (fuse_invalid_attr(&outarg.attr) ||
|
||||||
(inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
(inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
||||||
make_bad_inode(inode);
|
fuse_make_bad(inode);
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -1727,6 +1763,9 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
|
|||||||
struct file *file = (attr->ia_valid & ATTR_FILE) ? attr->ia_file : NULL;
|
struct file *file = (attr->ia_valid & ATTR_FILE) ? attr->ia_file : NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (fuse_is_bad(inode))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
if (!fuse_allow_current_process(get_fuse_conn(inode)))
|
if (!fuse_allow_current_process(get_fuse_conn(inode)))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
@@ -1740,7 +1779,7 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
|
|||||||
*
|
*
|
||||||
* This should be done on write(), truncate() and chown().
|
* This should be done on write(), truncate() and chown().
|
||||||
*/
|
*/
|
||||||
if (!fc->handle_killpriv) {
|
if (!fc->handle_killpriv && !fc->handle_killpriv_v2) {
|
||||||
/*
|
/*
|
||||||
* ia_mode calculation may have used stale i_mode.
|
* ia_mode calculation may have used stale i_mode.
|
||||||
* Refresh and recalculate.
|
* Refresh and recalculate.
|
||||||
@@ -1785,6 +1824,9 @@ static int fuse_getattr(const struct path *path, struct kstat *stat,
|
|||||||
struct inode *inode = d_inode(path->dentry);
|
struct inode *inode = d_inode(path->dentry);
|
||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
|
||||||
|
if (fuse_is_bad(inode))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
if (!fuse_allow_current_process(fc)) {
|
if (!fuse_allow_current_process(fc)) {
|
||||||
if (!request_mask) {
|
if (!request_mask) {
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid, struct file *file,
|
|||||||
inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
|
inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
|
||||||
if (!fm->fc->atomic_o_trunc)
|
if (!fm->fc->atomic_o_trunc)
|
||||||
inarg.flags &= ~O_TRUNC;
|
inarg.flags &= ~O_TRUNC;
|
||||||
|
|
||||||
|
if (fm->fc->handle_killpriv_v2 &&
|
||||||
|
(inarg.flags & O_TRUNC) && !capable(CAP_FSETID)) {
|
||||||
|
inarg.open_flags |= FUSE_OPEN_KILL_SUIDGID;
|
||||||
|
}
|
||||||
|
|
||||||
args.opcode = opcode;
|
args.opcode = opcode;
|
||||||
args.nodeid = nodeid;
|
args.nodeid = nodeid;
|
||||||
args.in_numargs = 1;
|
args.in_numargs = 1;
|
||||||
@@ -226,6 +232,9 @@ int fuse_open_common(struct inode *inode, struct file *file, bool isdir)
|
|||||||
bool dax_truncate = (file->f_flags & O_TRUNC) &&
|
bool dax_truncate = (file->f_flags & O_TRUNC) &&
|
||||||
fc->atomic_o_trunc && FUSE_IS_DAX(inode);
|
fc->atomic_o_trunc && FUSE_IS_DAX(inode);
|
||||||
|
|
||||||
|
if (fuse_is_bad(inode))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
err = generic_file_open(inode, file);
|
err = generic_file_open(inode, file);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
@@ -463,7 +472,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
|
|||||||
FUSE_ARGS(args);
|
FUSE_ARGS(args);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
err = write_inode_now(inode, 1);
|
err = write_inode_now(inode, 1);
|
||||||
@@ -535,7 +544,7 @@ static int fuse_fsync(struct file *file, loff_t start, loff_t end,
|
|||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
@@ -859,7 +868,7 @@ static int fuse_readpage(struct file *file, struct page *page)
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
err = fuse_do_readpage(file, page);
|
err = fuse_do_readpage(file, page);
|
||||||
@@ -952,7 +961,7 @@ static void fuse_readahead(struct readahead_control *rac)
|
|||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
unsigned int i, max_pages, nr_pages = 0;
|
unsigned int i, max_pages, nr_pages = 0;
|
||||||
|
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
max_pages = min_t(unsigned int, fc->max_pages,
|
max_pages = min_t(unsigned int, fc->max_pages,
|
||||||
@@ -1097,6 +1106,8 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
|
|||||||
|
|
||||||
fuse_write_args_fill(ia, ff, pos, count);
|
fuse_write_args_fill(ia, ff, pos, count);
|
||||||
ia->write.in.flags = fuse_write_flags(iocb);
|
ia->write.in.flags = fuse_write_flags(iocb);
|
||||||
|
if (fm->fc->handle_killpriv_v2 && !capable(CAP_FSETID))
|
||||||
|
ia->write.in.write_flags |= FUSE_WRITE_KILL_SUIDGID;
|
||||||
|
|
||||||
err = fuse_simple_request(fm, &ap->args);
|
err = fuse_simple_request(fm, &ap->args);
|
||||||
if (!err && ia->write.out.size > count)
|
if (!err && ia->write.out.size > count)
|
||||||
@@ -1260,17 +1271,24 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||||||
ssize_t written_buffered = 0;
|
ssize_t written_buffered = 0;
|
||||||
struct inode *inode = mapping->host;
|
struct inode *inode = mapping->host;
|
||||||
ssize_t err;
|
ssize_t err;
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
loff_t endbyte = 0;
|
loff_t endbyte = 0;
|
||||||
|
|
||||||
if (get_fuse_conn(inode)->writeback_cache) {
|
if (fc->writeback_cache) {
|
||||||
/* Update size (EOF optimization) and mode (SUID clearing) */
|
/* Update size (EOF optimization) and mode (SUID clearing) */
|
||||||
err = fuse_update_attributes(mapping->host, file);
|
err = fuse_update_attributes(mapping->host, file);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (fc->handle_killpriv_v2 &&
|
||||||
|
should_remove_suid(file_dentry(file))) {
|
||||||
|
goto writethrough;
|
||||||
|
}
|
||||||
|
|
||||||
return generic_file_write_iter(iocb, from);
|
return generic_file_write_iter(iocb, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writethrough:
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
|
|
||||||
/* We can write back this queue in page reclaim */
|
/* We can write back this queue in page reclaim */
|
||||||
@@ -1451,7 +1469,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
|
|||||||
|
|
||||||
if (write) {
|
if (write) {
|
||||||
if (!capable(CAP_FSETID))
|
if (!capable(CAP_FSETID))
|
||||||
ia->write.in.write_flags |= FUSE_WRITE_KILL_PRIV;
|
ia->write.in.write_flags |= FUSE_WRITE_KILL_SUIDGID;
|
||||||
|
|
||||||
nres = fuse_send_write(ia, pos, nbytes, owner);
|
nres = fuse_send_write(ia, pos, nbytes, owner);
|
||||||
} else {
|
} else {
|
||||||
@@ -1555,7 +1573,7 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
|||||||
struct fuse_file *ff = file->private_data;
|
struct fuse_file *ff = file->private_data;
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
|
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if (FUSE_IS_DAX(inode))
|
if (FUSE_IS_DAX(inode))
|
||||||
@@ -1573,7 +1591,7 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||||||
struct fuse_file *ff = file->private_data;
|
struct fuse_file *ff = file->private_data;
|
||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
|
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if (FUSE_IS_DAX(inode))
|
if (FUSE_IS_DAX(inode))
|
||||||
@@ -2172,7 +2190,7 @@ static int fuse_writepages(struct address_space *mapping,
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
data.inode = inode;
|
data.inode = inode;
|
||||||
@@ -2281,6 +2299,9 @@ static int fuse_launder_page(struct page *page)
|
|||||||
int err = 0;
|
int err = 0;
|
||||||
if (clear_page_dirty_for_io(page)) {
|
if (clear_page_dirty_for_io(page)) {
|
||||||
struct inode *inode = page->mapping->host;
|
struct inode *inode = page->mapping->host;
|
||||||
|
|
||||||
|
/* Serialize with pending writeback for the same page */
|
||||||
|
fuse_wait_on_page_writeback(inode, page->index);
|
||||||
err = fuse_writepage_locked(page);
|
err = fuse_writepage_locked(page);
|
||||||
if (!err)
|
if (!err)
|
||||||
fuse_wait_on_page_writeback(inode, page->index);
|
fuse_wait_on_page_writeback(inode, page->index);
|
||||||
@@ -2954,7 +2975,7 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd,
|
|||||||
if (!fuse_allow_current_process(fc))
|
if (!fuse_allow_current_process(fc))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
return fuse_do_ioctl(file, cmd, arg, flags);
|
return fuse_do_ioctl(file, cmd, arg, flags);
|
||||||
|
|||||||
@@ -172,6 +172,8 @@ enum {
|
|||||||
FUSE_I_INIT_RDPLUS,
|
FUSE_I_INIT_RDPLUS,
|
||||||
/** An operation changing file size is in progress */
|
/** An operation changing file size is in progress */
|
||||||
FUSE_I_SIZE_UNSTABLE,
|
FUSE_I_SIZE_UNSTABLE,
|
||||||
|
/* Bad inode */
|
||||||
|
FUSE_I_BAD,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_conn;
|
struct fuse_conn;
|
||||||
@@ -635,6 +637,14 @@ struct fuse_conn {
|
|||||||
/* show legacy mount options */
|
/* show legacy mount options */
|
||||||
unsigned int legacy_opts_show:1;
|
unsigned int legacy_opts_show:1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fs kills suid/sgid/cap on write/chown/trunc. suid is killed on
|
||||||
|
* write/trunc only if caller did not have CAP_FSETID. sgid is killed
|
||||||
|
* on write/truncate only if caller did not have CAP_FSETID as well as
|
||||||
|
* file has group execute permission.
|
||||||
|
*/
|
||||||
|
unsigned handle_killpriv_v2:1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following bitfields are only for optimization purposes
|
* The following bitfields are only for optimization purposes
|
||||||
* and hence races in setting them will not cause malfunction
|
* and hence races in setting them will not cause malfunction
|
||||||
@@ -801,9 +811,6 @@ struct fuse_mount {
|
|||||||
/* Underlying (potentially shared) connection to the FUSE server */
|
/* Underlying (potentially shared) connection to the FUSE server */
|
||||||
struct fuse_conn *fc;
|
struct fuse_conn *fc;
|
||||||
|
|
||||||
/* Refcount */
|
|
||||||
refcount_t count;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Super block for this connection (fc->killsb must be held when
|
* Super block for this connection (fc->killsb must be held when
|
||||||
* accessing this).
|
* accessing this).
|
||||||
@@ -821,9 +828,7 @@ static inline struct fuse_mount *get_fuse_mount_super(struct super_block *sb)
|
|||||||
|
|
||||||
static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
|
static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct fuse_mount *fm = get_fuse_mount_super(sb);
|
return get_fuse_mount_super(sb)->fc;
|
||||||
|
|
||||||
return fm ? fm->fc : NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct fuse_mount *get_fuse_mount(struct inode *inode)
|
static inline struct fuse_mount *get_fuse_mount(struct inode *inode)
|
||||||
@@ -833,9 +838,7 @@ static inline struct fuse_mount *get_fuse_mount(struct inode *inode)
|
|||||||
|
|
||||||
static inline struct fuse_conn *get_fuse_conn(struct inode *inode)
|
static inline struct fuse_conn *get_fuse_conn(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct fuse_mount *fm = get_fuse_mount(inode);
|
return get_fuse_mount_super(inode->i_sb)->fc;
|
||||||
|
|
||||||
return fm ? fm->fc : NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct fuse_inode *get_fuse_inode(struct inode *inode)
|
static inline struct fuse_inode *get_fuse_inode(struct inode *inode)
|
||||||
@@ -858,6 +861,16 @@ static inline u64 fuse_get_attr_version(struct fuse_conn *fc)
|
|||||||
return atomic64_read(&fc->attr_version);
|
return atomic64_read(&fc->attr_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void fuse_make_bad(struct inode *inode)
|
||||||
|
{
|
||||||
|
set_bit(FUSE_I_BAD, &get_fuse_inode(inode)->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool fuse_is_bad(struct inode *inode)
|
||||||
|
{
|
||||||
|
return unlikely(test_bit(FUSE_I_BAD, &get_fuse_inode(inode)->state));
|
||||||
|
}
|
||||||
|
|
||||||
/** Device operations */
|
/** Device operations */
|
||||||
extern const struct file_operations fuse_dev_operations;
|
extern const struct file_operations fuse_dev_operations;
|
||||||
|
|
||||||
@@ -1024,16 +1037,6 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
|
|||||||
*/
|
*/
|
||||||
void fuse_conn_put(struct fuse_conn *fc);
|
void fuse_conn_put(struct fuse_conn *fc);
|
||||||
|
|
||||||
/**
|
|
||||||
* Acquire reference to fuse_mount
|
|
||||||
*/
|
|
||||||
struct fuse_mount *fuse_mount_get(struct fuse_mount *fm);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release reference to fuse_mount
|
|
||||||
*/
|
|
||||||
void fuse_mount_put(struct fuse_mount *fm);
|
|
||||||
|
|
||||||
struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc);
|
struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc);
|
||||||
struct fuse_dev *fuse_dev_alloc(void);
|
struct fuse_dev *fuse_dev_alloc(void);
|
||||||
void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc);
|
void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc);
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ static void fuse_evict_inode(struct inode *inode)
|
|||||||
fi->forget = NULL;
|
fi->forget = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (S_ISREG(inode->i_mode) && !is_bad_inode(inode)) {
|
if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) {
|
||||||
WARN_ON(!list_empty(&fi->write_files));
|
WARN_ON(!list_empty(&fi->write_files));
|
||||||
WARN_ON(!list_empty(&fi->queued_writes));
|
WARN_ON(!list_empty(&fi->queued_writes));
|
||||||
}
|
}
|
||||||
@@ -204,6 +204,16 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
|
|||||||
inode->i_mode &= ~S_ISVTX;
|
inode->i_mode &= ~S_ISVTX;
|
||||||
|
|
||||||
fi->orig_ino = attr->ino;
|
fi->orig_ino = attr->ino;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We are refreshing inode data and it is possible that another
|
||||||
|
* client set suid/sgid or security.capability xattr. So clear
|
||||||
|
* S_NOSEC. Ideally, we could have cleared it only if suid/sgid
|
||||||
|
* was set or if security.capability xattr was set. But we don't
|
||||||
|
* know if security.capability has been set or not. So clear it
|
||||||
|
* anyway. Its less efficient but should be safe.
|
||||||
|
*/
|
||||||
|
inode->i_flags &= ~S_NOSEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
|
||||||
@@ -342,7 +352,7 @@ retry:
|
|||||||
unlock_new_inode(inode);
|
unlock_new_inode(inode);
|
||||||
} else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
|
} else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
|
||||||
/* Inode has changed type, any I/O on the old should fail */
|
/* Inode has changed type, any I/O on the old should fail */
|
||||||
make_bad_inode(inode);
|
fuse_make_bad(inode);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
@@ -452,7 +462,8 @@ static void fuse_put_super(struct super_block *sb)
|
|||||||
{
|
{
|
||||||
struct fuse_mount *fm = get_fuse_mount_super(sb);
|
struct fuse_mount *fm = get_fuse_mount_super(sb);
|
||||||
|
|
||||||
fuse_mount_put(fm);
|
fuse_conn_put(fm->fc);
|
||||||
|
kfree(fm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr)
|
static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr)
|
||||||
@@ -705,7 +716,6 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
|
|||||||
INIT_LIST_HEAD(&fc->mounts);
|
INIT_LIST_HEAD(&fc->mounts);
|
||||||
list_add(&fm->fc_entry, &fc->mounts);
|
list_add(&fm->fc_entry, &fc->mounts);
|
||||||
fm->fc = fc;
|
fm->fc = fc;
|
||||||
refcount_set(&fm->count, 1);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fuse_conn_init);
|
EXPORT_SYMBOL_GPL(fuse_conn_init);
|
||||||
|
|
||||||
@@ -732,23 +742,6 @@ struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fuse_conn_get);
|
EXPORT_SYMBOL_GPL(fuse_conn_get);
|
||||||
|
|
||||||
void fuse_mount_put(struct fuse_mount *fm)
|
|
||||||
{
|
|
||||||
if (refcount_dec_and_test(&fm->count)) {
|
|
||||||
if (fm->fc)
|
|
||||||
fuse_conn_put(fm->fc);
|
|
||||||
kfree(fm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(fuse_mount_put);
|
|
||||||
|
|
||||||
struct fuse_mount *fuse_mount_get(struct fuse_mount *fm)
|
|
||||||
{
|
|
||||||
refcount_inc(&fm->count);
|
|
||||||
return fm;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(fuse_mount_get);
|
|
||||||
|
|
||||||
static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
|
static struct inode *fuse_get_root_inode(struct super_block *sb, unsigned mode)
|
||||||
{
|
{
|
||||||
struct fuse_attr attr;
|
struct fuse_attr attr;
|
||||||
@@ -1055,6 +1048,10 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
|||||||
!fuse_dax_check_alignment(fc, arg->map_alignment)) {
|
!fuse_dax_check_alignment(fc, arg->map_alignment)) {
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
|
if (arg->flags & FUSE_HANDLE_KILLPRIV_V2) {
|
||||||
|
fc->handle_killpriv_v2 = 1;
|
||||||
|
fm->sb->s_flags |= SB_NOSEC;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ra_pages = fc->max_read / PAGE_SIZE;
|
ra_pages = fc->max_read / PAGE_SIZE;
|
||||||
fc->no_lock = 1;
|
fc->no_lock = 1;
|
||||||
@@ -1097,7 +1094,8 @@ void fuse_send_init(struct fuse_mount *fm)
|
|||||||
FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
|
FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
|
||||||
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
|
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
|
||||||
FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
|
FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
|
||||||
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA;
|
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
|
||||||
|
FUSE_HANDLE_KILLPRIV_V2;
|
||||||
#ifdef CONFIG_FUSE_DAX
|
#ifdef CONFIG_FUSE_DAX
|
||||||
if (fm->fc->dax)
|
if (fm->fc->dax)
|
||||||
ia->in.flags |= FUSE_MAP_ALIGNMENT;
|
ia->in.flags |= FUSE_MAP_ALIGNMENT;
|
||||||
@@ -1465,7 +1463,8 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_put_conn:
|
err_put_conn:
|
||||||
fuse_mount_put(fm);
|
fuse_conn_put(fc);
|
||||||
|
kfree(fm);
|
||||||
sb->s_fs_info = NULL;
|
sb->s_fs_info = NULL;
|
||||||
err_fput:
|
err_fput:
|
||||||
fput(file);
|
fput(file);
|
||||||
@@ -1557,7 +1556,7 @@ void fuse_conn_destroy(struct fuse_mount *fm)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(fuse_conn_destroy);
|
EXPORT_SYMBOL_GPL(fuse_conn_destroy);
|
||||||
|
|
||||||
static void fuse_kill_sb_anon(struct super_block *sb)
|
static void fuse_sb_destroy(struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct fuse_mount *fm = get_fuse_mount_super(sb);
|
struct fuse_mount *fm = get_fuse_mount_super(sb);
|
||||||
bool last;
|
bool last;
|
||||||
@@ -1567,6 +1566,11 @@ static void fuse_kill_sb_anon(struct super_block *sb)
|
|||||||
if (last)
|
if (last)
|
||||||
fuse_conn_destroy(fm);
|
fuse_conn_destroy(fm);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fuse_kill_sb_anon(struct super_block *sb)
|
||||||
|
{
|
||||||
|
fuse_sb_destroy(sb);
|
||||||
kill_anon_super(sb);
|
kill_anon_super(sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1583,14 +1587,7 @@ MODULE_ALIAS_FS("fuse");
|
|||||||
#ifdef CONFIG_BLOCK
|
#ifdef CONFIG_BLOCK
|
||||||
static void fuse_kill_sb_blk(struct super_block *sb)
|
static void fuse_kill_sb_blk(struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct fuse_mount *fm = get_fuse_mount_super(sb);
|
fuse_sb_destroy(sb);
|
||||||
bool last;
|
|
||||||
|
|
||||||
if (fm) {
|
|
||||||
last = fuse_mount_remove(fm);
|
|
||||||
if (last)
|
|
||||||
fuse_conn_destroy(fm);
|
|
||||||
}
|
|
||||||
kill_block_super(sb);
|
kill_block_super(sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ retry:
|
|||||||
dput(dentry);
|
dput(dentry);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
if (is_bad_inode(inode)) {
|
if (fuse_is_bad(inode)) {
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
@@ -568,7 +568,7 @@ int fuse_readdir(struct file *file, struct dir_context *ctx)
|
|||||||
struct inode *inode = file_inode(file);
|
struct inode *inode = file_inode(file);
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (is_bad_inode(inode))
|
if (fuse_is_bad(inode))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
mutex_lock(&ff->readdir.lock);
|
mutex_lock(&ff->readdir.lock);
|
||||||
|
|||||||
@@ -1402,18 +1402,6 @@ static int virtio_fs_test_super(struct super_block *sb,
|
|||||||
return fsc_fm->fc->iq.priv == sb_fm->fc->iq.priv;
|
return fsc_fm->fc->iq.priv == sb_fm->fc->iq.priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int virtio_fs_set_super(struct super_block *sb,
|
|
||||||
struct fs_context *fsc)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = get_anon_bdev(&sb->s_dev);
|
|
||||||
if (!err)
|
|
||||||
fuse_mount_get(fsc->s_fs_info);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int virtio_fs_get_tree(struct fs_context *fsc)
|
static int virtio_fs_get_tree(struct fs_context *fsc)
|
||||||
{
|
{
|
||||||
struct virtio_fs *fs;
|
struct virtio_fs *fs;
|
||||||
@@ -1432,22 +1420,14 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = -ENOMEM;
|
||||||
fc = kzalloc(sizeof(struct fuse_conn), GFP_KERNEL);
|
fc = kzalloc(sizeof(struct fuse_conn), GFP_KERNEL);
|
||||||
if (!fc) {
|
if (!fc)
|
||||||
mutex_lock(&virtio_fs_mutex);
|
goto out_err;
|
||||||
virtio_fs_put(fs);
|
|
||||||
mutex_unlock(&virtio_fs_mutex);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
|
fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
|
||||||
if (!fm) {
|
if (!fm)
|
||||||
mutex_lock(&virtio_fs_mutex);
|
goto out_err;
|
||||||
virtio_fs_put(fs);
|
|
||||||
mutex_unlock(&virtio_fs_mutex);
|
|
||||||
kfree(fc);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
fuse_conn_init(fc, fm, get_user_ns(current_user_ns()),
|
fuse_conn_init(fc, fm, get_user_ns(current_user_ns()),
|
||||||
&virtio_fs_fiq_ops, fs);
|
&virtio_fs_fiq_ops, fs);
|
||||||
@@ -1456,14 +1436,20 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
|
|||||||
fc->auto_submounts = true;
|
fc->auto_submounts = true;
|
||||||
|
|
||||||
fsc->s_fs_info = fm;
|
fsc->s_fs_info = fm;
|
||||||
sb = sget_fc(fsc, virtio_fs_test_super, virtio_fs_set_super);
|
sb = sget_fc(fsc, virtio_fs_test_super, set_anon_super_fc);
|
||||||
fuse_mount_put(fm);
|
if (fsc->s_fs_info) {
|
||||||
|
fuse_conn_put(fc);
|
||||||
|
kfree(fm);
|
||||||
|
}
|
||||||
if (IS_ERR(sb))
|
if (IS_ERR(sb))
|
||||||
return PTR_ERR(sb);
|
return PTR_ERR(sb);
|
||||||
|
|
||||||
if (!sb->s_root) {
|
if (!sb->s_root) {
|
||||||
err = virtio_fs_fill_super(sb, fsc);
|
err = virtio_fs_fill_super(sb, fsc);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
fuse_conn_put(fc);
|
||||||
|
kfree(fm);
|
||||||
|
sb->s_fs_info = NULL;
|
||||||
deactivate_locked_super(sb);
|
deactivate_locked_super(sb);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -1474,6 +1460,13 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
|
|||||||
WARN_ON(fsc->root);
|
WARN_ON(fsc->root);
|
||||||
fsc->root = dget(sb->s_root);
|
fsc->root = dget(sb->s_root);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_err:
|
||||||
|
kfree(fc);
|
||||||
|
mutex_lock(&virtio_fs_mutex);
|
||||||
|
virtio_fs_put(fs);
|
||||||
|
mutex_unlock(&virtio_fs_mutex);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct fs_context_operations virtio_fs_context_ops = {
|
static const struct fs_context_operations virtio_fs_context_ops = {
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
|
|||||||
struct fuse_getxattr_out outarg;
|
struct fuse_getxattr_out outarg;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (fuse_is_bad(inode))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
if (!fuse_allow_current_process(fm->fc))
|
if (!fuse_allow_current_process(fm->fc))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
@@ -178,6 +181,9 @@ static int fuse_xattr_get(const struct xattr_handler *handler,
|
|||||||
struct dentry *dentry, struct inode *inode,
|
struct dentry *dentry, struct inode *inode,
|
||||||
const char *name, void *value, size_t size)
|
const char *name, void *value, size_t size)
|
||||||
{
|
{
|
||||||
|
if (fuse_is_bad(inode))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
return fuse_getxattr(inode, name, value, size);
|
return fuse_getxattr(inode, name, value, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +192,9 @@ static int fuse_xattr_set(const struct xattr_handler *handler,
|
|||||||
const char *name, const void *value, size_t size,
|
const char *name, const void *value, size_t size,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
|
if (fuse_is_bad(inode))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
return fuse_removexattr(inode, name);
|
return fuse_removexattr(inode, name);
|
||||||
|
|
||||||
|
|||||||
@@ -175,6 +175,10 @@
|
|||||||
*
|
*
|
||||||
* 7.32
|
* 7.32
|
||||||
* - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
|
* - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS
|
||||||
|
*
|
||||||
|
* 7.33
|
||||||
|
* - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
|
||||||
|
* - add FUSE_OPEN_KILL_SUIDGID
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LINUX_FUSE_H
|
#ifndef _LINUX_FUSE_H
|
||||||
@@ -210,7 +214,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 32
|
#define FUSE_KERNEL_MINOR_VERSION 33
|
||||||
|
|
||||||
/** The node ID of the root inode */
|
/** The node ID of the root inode */
|
||||||
#define FUSE_ROOT_ID 1
|
#define FUSE_ROOT_ID 1
|
||||||
@@ -271,6 +275,7 @@ struct fuse_file_lock {
|
|||||||
#define FATTR_MTIME_NOW (1 << 8)
|
#define FATTR_MTIME_NOW (1 << 8)
|
||||||
#define FATTR_LOCKOWNER (1 << 9)
|
#define FATTR_LOCKOWNER (1 << 9)
|
||||||
#define FATTR_CTIME (1 << 10)
|
#define FATTR_CTIME (1 << 10)
|
||||||
|
#define FATTR_KILL_SUIDGID (1 << 11)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flags returned by the OPEN request
|
* Flags returned by the OPEN request
|
||||||
@@ -320,6 +325,11 @@ struct fuse_file_lock {
|
|||||||
* foffset and moffset fields in struct
|
* foffset and moffset fields in struct
|
||||||
* fuse_setupmapping_out and fuse_removemapping_one.
|
* fuse_setupmapping_out and fuse_removemapping_one.
|
||||||
* FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts
|
* FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts
|
||||||
|
* FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc.
|
||||||
|
* Upon write/truncate suid/sgid is only killed if caller
|
||||||
|
* does not have CAP_FSETID. Additionally upon
|
||||||
|
* write/truncate sgid is killed only if file has group
|
||||||
|
* execute permission. (Same as Linux VFS behavior).
|
||||||
*/
|
*/
|
||||||
#define FUSE_ASYNC_READ (1 << 0)
|
#define FUSE_ASYNC_READ (1 << 0)
|
||||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||||
@@ -349,6 +359,7 @@ struct fuse_file_lock {
|
|||||||
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
|
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
|
||||||
#define FUSE_MAP_ALIGNMENT (1 << 26)
|
#define FUSE_MAP_ALIGNMENT (1 << 26)
|
||||||
#define FUSE_SUBMOUNTS (1 << 27)
|
#define FUSE_SUBMOUNTS (1 << 27)
|
||||||
|
#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CUSE INIT request/reply flags
|
* CUSE INIT request/reply flags
|
||||||
@@ -378,11 +389,14 @@ struct fuse_file_lock {
|
|||||||
*
|
*
|
||||||
* FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
|
* FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed
|
||||||
* FUSE_WRITE_LOCKOWNER: lock_owner field is valid
|
* FUSE_WRITE_LOCKOWNER: lock_owner field is valid
|
||||||
* FUSE_WRITE_KILL_PRIV: kill suid and sgid bits
|
* FUSE_WRITE_KILL_SUIDGID: kill suid and sgid bits
|
||||||
*/
|
*/
|
||||||
#define FUSE_WRITE_CACHE (1 << 0)
|
#define FUSE_WRITE_CACHE (1 << 0)
|
||||||
#define FUSE_WRITE_LOCKOWNER (1 << 1)
|
#define FUSE_WRITE_LOCKOWNER (1 << 1)
|
||||||
#define FUSE_WRITE_KILL_PRIV (1 << 2)
|
#define FUSE_WRITE_KILL_SUIDGID (1 << 2)
|
||||||
|
|
||||||
|
/* Obsolete alias; this flag implies killing suid/sgid only. */
|
||||||
|
#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read flags
|
* Read flags
|
||||||
@@ -431,6 +445,12 @@ struct fuse_file_lock {
|
|||||||
*/
|
*/
|
||||||
#define FUSE_ATTR_SUBMOUNT (1 << 0)
|
#define FUSE_ATTR_SUBMOUNT (1 << 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open flags
|
||||||
|
* FUSE_OPEN_KILL_SUIDGID: Kill suid and sgid if executable
|
||||||
|
*/
|
||||||
|
#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
|
||||||
|
|
||||||
enum fuse_opcode {
|
enum fuse_opcode {
|
||||||
FUSE_LOOKUP = 1,
|
FUSE_LOOKUP = 1,
|
||||||
FUSE_FORGET = 2, /* no reply */
|
FUSE_FORGET = 2, /* no reply */
|
||||||
@@ -592,14 +612,14 @@ struct fuse_setattr_in {
|
|||||||
|
|
||||||
struct fuse_open_in {
|
struct fuse_open_in {
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t unused;
|
uint32_t open_flags; /* FUSE_OPEN_... */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_create_in {
|
struct fuse_create_in {
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t mode;
|
uint32_t mode;
|
||||||
uint32_t umask;
|
uint32_t umask;
|
||||||
uint32_t padding;
|
uint32_t open_flags; /* FUSE_OPEN_... */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_open_out {
|
struct fuse_open_out {
|
||||||
|
|||||||
Reference in New Issue
Block a user