fuse update for 5.13
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCYIwY/wAKCRDh3BK/laaZ PNSmAPwLFCBGegvwxUSguiPmIXpDrrlG+USwTzGlxhVOg2ETGgEA6D+Lsz2uCBI3 xLkPAXD6uTbWLp13YtUSMXK+LR8V5wc= =Fl+Q -----END PGP SIGNATURE----- Merge tag 'fuse-update-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse Pull fuse updates from Miklos Szeredi: - Fix a page locking bug in write (introduced in 2.6.26) - Allow sgid bit to be killed in setacl() - Miscellaneous fixes and cleanups * tag 'fuse-update-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: cuse: simplify refcount cuse: prevent clone virtiofs: fix userns virtiofs: remove useless function virtiofs: split requests that exceed virtqueue size virtiofs: fix memory leak in virtio_fs_probe() fuse: invalidate attrs when page writeback completes fuse: add a flag FUSE_SETXATTR_ACL_KILL_SGID to kill SGID fuse: extend FUSE_SETXATTR request fuse: fix matching of FUSE_DEV_IOC_CLONE command fuse: fix a typo fuse: don't zero pages twice fuse: fix typo for fuse_conn.max_pages comment fuse: fix write deadlock
This commit is contained in:
commit
9ec1efbf9d
|
@ -71,6 +71,7 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (acl) {
|
if (acl) {
|
||||||
|
unsigned int extra_flags = 0;
|
||||||
/*
|
/*
|
||||||
* Fuse userspace is responsible for updating access
|
* Fuse userspace is responsible for updating access
|
||||||
* permissions in the inode, if needed. fuse_setxattr
|
* permissions in the inode, if needed. fuse_setxattr
|
||||||
|
@ -94,7 +95,11 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = fuse_setxattr(inode, name, value, size, 0);
|
if (!in_group_p(i_gid_into_mnt(&init_user_ns, inode)) &&
|
||||||
|
!capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID))
|
||||||
|
extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID;
|
||||||
|
|
||||||
|
ret = fuse_setxattr(inode, name, value, size, 0, extra_flags);
|
||||||
kfree(value);
|
kfree(value);
|
||||||
} else {
|
} else {
|
||||||
ret = fuse_removexattr(inode, name);
|
ret = fuse_removexattr(inode, name);
|
||||||
|
|
|
@ -511,20 +511,18 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
|
||||||
fuse_conn_init(&cc->fc, &cc->fm, file->f_cred->user_ns,
|
fuse_conn_init(&cc->fc, &cc->fm, file->f_cred->user_ns,
|
||||||
&fuse_dev_fiq_ops, NULL);
|
&fuse_dev_fiq_ops, NULL);
|
||||||
|
|
||||||
|
cc->fc.release = cuse_fc_release;
|
||||||
fud = fuse_dev_alloc_install(&cc->fc);
|
fud = fuse_dev_alloc_install(&cc->fc);
|
||||||
if (!fud) {
|
fuse_conn_put(&cc->fc);
|
||||||
kfree(cc);
|
if (!fud)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
INIT_LIST_HEAD(&cc->list);
|
INIT_LIST_HEAD(&cc->list);
|
||||||
cc->fc.release = cuse_fc_release;
|
|
||||||
|
|
||||||
cc->fc.initialized = 1;
|
cc->fc.initialized = 1;
|
||||||
rc = cuse_send_init(cc);
|
rc = cuse_send_init(cc);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
fuse_dev_free(fud);
|
fuse_dev_free(fud);
|
||||||
fuse_conn_put(&cc->fc);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
file->private_data = fud;
|
file->private_data = fud;
|
||||||
|
@ -561,8 +559,6 @@ static int cuse_channel_release(struct inode *inode, struct file *file)
|
||||||
unregister_chrdev_region(cc->cdev->dev, 1);
|
unregister_chrdev_region(cc->cdev->dev, 1);
|
||||||
cdev_del(cc->cdev);
|
cdev_del(cc->cdev);
|
||||||
}
|
}
|
||||||
/* Base reference is now owned by "fud" */
|
|
||||||
fuse_conn_put(&cc->fc);
|
|
||||||
|
|
||||||
rc = fuse_dev_release(inode, file); /* puts the base reference */
|
rc = fuse_dev_release(inode, file); /* puts the base reference */
|
||||||
|
|
||||||
|
@ -627,6 +623,8 @@ static int __init cuse_init(void)
|
||||||
cuse_channel_fops.owner = THIS_MODULE;
|
cuse_channel_fops.owner = THIS_MODULE;
|
||||||
cuse_channel_fops.open = cuse_channel_open;
|
cuse_channel_fops.open = cuse_channel_open;
|
||||||
cuse_channel_fops.release = cuse_channel_release;
|
cuse_channel_fops.release = cuse_channel_release;
|
||||||
|
/* CUSE is not prepared for FUSE_DEV_IOC_CLONE */
|
||||||
|
cuse_channel_fops.unlocked_ioctl = NULL;
|
||||||
|
|
||||||
cuse_class = class_create(THIS_MODULE, "cuse");
|
cuse_class = class_create(THIS_MODULE, "cuse");
|
||||||
if (IS_ERR(cuse_class))
|
if (IS_ERR(cuse_class))
|
||||||
|
|
|
@ -2233,11 +2233,8 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
|
||||||
int oldfd;
|
int oldfd;
|
||||||
struct fuse_dev *fud = NULL;
|
struct fuse_dev *fud = NULL;
|
||||||
|
|
||||||
if (_IOC_TYPE(cmd) != FUSE_DEV_IOC_MAGIC)
|
switch (cmd) {
|
||||||
return -ENOTTY;
|
case FUSE_DEV_IOC_CLONE:
|
||||||
|
|
||||||
switch (_IOC_NR(cmd)) {
|
|
||||||
case _IOC_NR(FUSE_DEV_IOC_CLONE):
|
|
||||||
res = -EFAULT;
|
res = -EFAULT;
|
||||||
if (!get_user(oldfd, (__u32 __user *)arg)) {
|
if (!get_user(oldfd, (__u32 __user *)arg)) {
|
||||||
struct file *old = fget(oldfd);
|
struct file *old = fget(oldfd);
|
||||||
|
|
|
@ -802,21 +802,12 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read,
|
||||||
{
|
{
|
||||||
struct fuse_conn *fc = get_fuse_conn(inode);
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
|
||||||
if (fc->writeback_cache) {
|
/*
|
||||||
/*
|
* If writeback_cache is enabled, a short read means there's a hole in
|
||||||
* A hole in a file. Some data after the hole are in page cache,
|
* the file. Some data after the hole is in page cache, but has not
|
||||||
* but have not reached the client fs yet. So, the hole is not
|
* reached the client fs yet. So the hole is not present there.
|
||||||
* present there.
|
*/
|
||||||
*/
|
if (!fc->writeback_cache) {
|
||||||
int i;
|
|
||||||
int start_idx = num_read >> PAGE_SHIFT;
|
|
||||||
size_t off = num_read & (PAGE_SIZE - 1);
|
|
||||||
|
|
||||||
for (i = start_idx; i < ap->num_pages; i++) {
|
|
||||||
zero_user_segment(ap->pages[i], off, PAGE_SIZE);
|
|
||||||
off = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loff_t pos = page_offset(ap->pages[0]) + num_read;
|
loff_t pos = page_offset(ap->pages[0]) + num_read;
|
||||||
fuse_read_update_size(inode, pos, attr_ver);
|
fuse_read_update_size(inode, pos, attr_ver);
|
||||||
}
|
}
|
||||||
|
@ -1103,6 +1094,7 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
|
||||||
struct fuse_file *ff = file->private_data;
|
struct fuse_file *ff = file->private_data;
|
||||||
struct fuse_mount *fm = ff->fm;
|
struct fuse_mount *fm = ff->fm;
|
||||||
unsigned int offset, i;
|
unsigned int offset, i;
|
||||||
|
bool short_write;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
for (i = 0; i < ap->num_pages; i++)
|
for (i = 0; i < ap->num_pages; i++)
|
||||||
|
@ -1117,32 +1109,38 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
|
||||||
if (!err && ia->write.out.size > count)
|
if (!err && ia->write.out.size > count)
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
|
|
||||||
|
short_write = ia->write.out.size < count;
|
||||||
offset = ap->descs[0].offset;
|
offset = ap->descs[0].offset;
|
||||||
count = ia->write.out.size;
|
count = ia->write.out.size;
|
||||||
for (i = 0; i < ap->num_pages; i++) {
|
for (i = 0; i < ap->num_pages; i++) {
|
||||||
struct page *page = ap->pages[i];
|
struct page *page = ap->pages[i];
|
||||||
|
|
||||||
if (!err && !offset && count >= PAGE_SIZE)
|
if (err) {
|
||||||
SetPageUptodate(page);
|
ClearPageUptodate(page);
|
||||||
|
} else {
|
||||||
if (count > PAGE_SIZE - offset)
|
if (count >= PAGE_SIZE - offset)
|
||||||
count -= PAGE_SIZE - offset;
|
count -= PAGE_SIZE - offset;
|
||||||
else
|
else {
|
||||||
count = 0;
|
if (short_write)
|
||||||
offset = 0;
|
ClearPageUptodate(page);
|
||||||
|
count = 0;
|
||||||
unlock_page(page);
|
}
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
if (ia->write.page_locked && (i == ap->num_pages - 1))
|
||||||
|
unlock_page(page);
|
||||||
put_page(page);
|
put_page(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t fuse_fill_write_pages(struct fuse_args_pages *ap,
|
static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia,
|
||||||
struct address_space *mapping,
|
struct address_space *mapping,
|
||||||
struct iov_iter *ii, loff_t pos,
|
struct iov_iter *ii, loff_t pos,
|
||||||
unsigned int max_pages)
|
unsigned int max_pages)
|
||||||
{
|
{
|
||||||
|
struct fuse_args_pages *ap = &ia->ap;
|
||||||
struct fuse_conn *fc = get_fuse_conn(mapping->host);
|
struct fuse_conn *fc = get_fuse_conn(mapping->host);
|
||||||
unsigned offset = pos & (PAGE_SIZE - 1);
|
unsigned offset = pos & (PAGE_SIZE - 1);
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
|
@ -1195,6 +1193,16 @@ static ssize_t fuse_fill_write_pages(struct fuse_args_pages *ap,
|
||||||
if (offset == PAGE_SIZE)
|
if (offset == PAGE_SIZE)
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
|
/* If we copied full page, mark it uptodate */
|
||||||
|
if (tmp == PAGE_SIZE)
|
||||||
|
SetPageUptodate(page);
|
||||||
|
|
||||||
|
if (PageUptodate(page)) {
|
||||||
|
unlock_page(page);
|
||||||
|
} else {
|
||||||
|
ia->write.page_locked = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (!fc->big_writes)
|
if (!fc->big_writes)
|
||||||
break;
|
break;
|
||||||
} while (iov_iter_count(ii) && count < fc->max_write &&
|
} while (iov_iter_count(ii) && count < fc->max_write &&
|
||||||
|
@ -1238,7 +1246,7 @@ static ssize_t fuse_perform_write(struct kiocb *iocb,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = fuse_fill_write_pages(ap, mapping, ii, pos, nr_pages);
|
count = fuse_fill_write_pages(&ia, mapping, ii, pos, nr_pages);
|
||||||
if (count <= 0) {
|
if (count <= 0) {
|
||||||
err = count;
|
err = count;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1753,8 +1761,17 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
|
||||||
container_of(args, typeof(*wpa), ia.ap.args);
|
container_of(args, typeof(*wpa), ia.ap.args);
|
||||||
struct inode *inode = wpa->inode;
|
struct inode *inode = wpa->inode;
|
||||||
struct fuse_inode *fi = get_fuse_inode(inode);
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||||
|
struct fuse_conn *fc = get_fuse_conn(inode);
|
||||||
|
|
||||||
mapping_set_error(inode->i_mapping, error);
|
mapping_set_error(inode->i_mapping, error);
|
||||||
|
/*
|
||||||
|
* A writeback finished and this might have updated mtime/ctime on
|
||||||
|
* server making local mtime/ctime stale. Hence invalidate attrs.
|
||||||
|
* Do this only if writeback_cache is not enabled. If writeback_cache
|
||||||
|
* is enabled, we trust local ctime/mtime.
|
||||||
|
*/
|
||||||
|
if (!fc->writeback_cache)
|
||||||
|
fuse_invalidate_attr(inode);
|
||||||
spin_lock(&fi->lock);
|
spin_lock(&fi->lock);
|
||||||
rb_erase(&wpa->writepages_entry, &fi->writepages);
|
rb_erase(&wpa->writepages_entry, &fi->writepages);
|
||||||
while (wpa->next) {
|
while (wpa->next) {
|
||||||
|
|
|
@ -552,9 +552,12 @@ struct fuse_conn {
|
||||||
/** Maximum write size */
|
/** Maximum write size */
|
||||||
unsigned max_write;
|
unsigned max_write;
|
||||||
|
|
||||||
/** Maxmum number of pages that can be used in a single request */
|
/** Maximum number of pages that can be used in a single request */
|
||||||
unsigned int max_pages;
|
unsigned int max_pages;
|
||||||
|
|
||||||
|
/** Constrain ->max_pages to this value during feature negotiation */
|
||||||
|
unsigned int max_pages_limit;
|
||||||
|
|
||||||
/** Input queue */
|
/** Input queue */
|
||||||
struct fuse_iqueue iq;
|
struct fuse_iqueue iq;
|
||||||
|
|
||||||
|
@ -668,6 +671,9 @@ struct fuse_conn {
|
||||||
/** Is setxattr not implemented by fs? */
|
/** Is setxattr not implemented by fs? */
|
||||||
unsigned no_setxattr:1;
|
unsigned no_setxattr:1;
|
||||||
|
|
||||||
|
/** Does file server support extended setxattr */
|
||||||
|
unsigned setxattr_ext:1;
|
||||||
|
|
||||||
/** Is getxattr not implemented by fs? */
|
/** Is getxattr not implemented by fs? */
|
||||||
unsigned no_getxattr:1;
|
unsigned no_getxattr:1;
|
||||||
|
|
||||||
|
@ -713,7 +719,7 @@ struct fuse_conn {
|
||||||
/** Use enhanced/automatic page cache invalidation. */
|
/** Use enhanced/automatic page cache invalidation. */
|
||||||
unsigned auto_inval_data:1;
|
unsigned auto_inval_data:1;
|
||||||
|
|
||||||
/** Filesystem is fully reponsible for page cache invalidation. */
|
/** Filesystem is fully responsible for page cache invalidation. */
|
||||||
unsigned explicit_inval_data:1;
|
unsigned explicit_inval_data:1;
|
||||||
|
|
||||||
/** Does the filesystem support readdirplus? */
|
/** Does the filesystem support readdirplus? */
|
||||||
|
@ -934,6 +940,7 @@ struct fuse_io_args {
|
||||||
struct {
|
struct {
|
||||||
struct fuse_write_in in;
|
struct fuse_write_in in;
|
||||||
struct fuse_write_out out;
|
struct fuse_write_out out;
|
||||||
|
bool page_locked;
|
||||||
} write;
|
} write;
|
||||||
};
|
};
|
||||||
struct fuse_args_pages ap;
|
struct fuse_args_pages ap;
|
||||||
|
@ -1193,7 +1200,7 @@ void fuse_unlock_inode(struct inode *inode, bool locked);
|
||||||
bool fuse_lock_inode(struct inode *inode);
|
bool fuse_lock_inode(struct inode *inode);
|
||||||
|
|
||||||
int fuse_setxattr(struct inode *inode, const char *name, const void *value,
|
int fuse_setxattr(struct inode *inode, const char *name, const void *value,
|
||||||
size_t size, int flags);
|
size_t size, int flags, unsigned int extra_flags);
|
||||||
ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
|
ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
|
||||||
size_t size);
|
size_t size);
|
||||||
ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size);
|
ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size);
|
||||||
|
|
|
@ -712,6 +712,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
|
||||||
fc->pid_ns = get_pid_ns(task_active_pid_ns(current));
|
fc->pid_ns = get_pid_ns(task_active_pid_ns(current));
|
||||||
fc->user_ns = get_user_ns(user_ns);
|
fc->user_ns = get_user_ns(user_ns);
|
||||||
fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
|
fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
|
||||||
|
fc->max_pages_limit = FUSE_MAX_MAX_PAGES;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&fc->mounts);
|
INIT_LIST_HEAD(&fc->mounts);
|
||||||
list_add(&fm->fc_entry, &fc->mounts);
|
list_add(&fm->fc_entry, &fc->mounts);
|
||||||
|
@ -1040,7 +1041,7 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||||
fc->abort_err = 1;
|
fc->abort_err = 1;
|
||||||
if (arg->flags & FUSE_MAX_PAGES) {
|
if (arg->flags & FUSE_MAX_PAGES) {
|
||||||
fc->max_pages =
|
fc->max_pages =
|
||||||
min_t(unsigned int, FUSE_MAX_MAX_PAGES,
|
min_t(unsigned int, fc->max_pages_limit,
|
||||||
max_t(unsigned int, arg->max_pages, 1));
|
max_t(unsigned int, arg->max_pages, 1));
|
||||||
}
|
}
|
||||||
if (IS_ENABLED(CONFIG_FUSE_DAX) &&
|
if (IS_ENABLED(CONFIG_FUSE_DAX) &&
|
||||||
|
@ -1052,6 +1053,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
|
||||||
fc->handle_killpriv_v2 = 1;
|
fc->handle_killpriv_v2 = 1;
|
||||||
fm->sb->s_flags |= SB_NOSEC;
|
fm->sb->s_flags |= SB_NOSEC;
|
||||||
}
|
}
|
||||||
|
if (arg->flags & FUSE_SETXATTR_EXT)
|
||||||
|
fc->setxattr_ext = 1;
|
||||||
} else {
|
} else {
|
||||||
ra_pages = fc->max_read / PAGE_SIZE;
|
ra_pages = fc->max_read / PAGE_SIZE;
|
||||||
fc->no_lock = 1;
|
fc->no_lock = 1;
|
||||||
|
@ -1095,7 +1098,7 @@ void fuse_send_init(struct fuse_mount *fm)
|
||||||
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;
|
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT;
|
||||||
#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;
|
||||||
|
|
|
@ -18,6 +18,12 @@
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
#include "fuse_i.h"
|
#include "fuse_i.h"
|
||||||
|
|
||||||
|
/* Used to help calculate the FUSE connection's max_pages limit for a request's
|
||||||
|
* size. Parts of the struct fuse_req are sliced into scattergather lists in
|
||||||
|
* addition to the pages used, so this can help account for that overhead.
|
||||||
|
*/
|
||||||
|
#define FUSE_HEADER_OVERHEAD 4
|
||||||
|
|
||||||
/* List of virtio-fs device instances and a lock for the list. Also provides
|
/* List of virtio-fs device instances and a lock for the list. Also provides
|
||||||
* mutual exclusion in device removal and mounting path
|
* mutual exclusion in device removal and mounting path
|
||||||
*/
|
*/
|
||||||
|
@ -127,11 +133,6 @@ static inline struct virtio_fs_vq *vq_to_fsvq(struct virtqueue *vq)
|
||||||
return &fs->vqs[vq->index];
|
return &fs->vqs[vq->index];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct fuse_pqueue *vq_to_fpq(struct virtqueue *vq)
|
|
||||||
{
|
|
||||||
return &vq_to_fsvq(vq)->fud->pq;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Should be called with fsvq->lock held. */
|
/* Should be called with fsvq->lock held. */
|
||||||
static inline void inc_in_flight_req(struct virtio_fs_vq *fsvq)
|
static inline void inc_in_flight_req(struct virtio_fs_vq *fsvq)
|
||||||
{
|
{
|
||||||
|
@ -896,6 +897,7 @@ static int virtio_fs_probe(struct virtio_device *vdev)
|
||||||
out_vqs:
|
out_vqs:
|
||||||
vdev->config->reset(vdev);
|
vdev->config->reset(vdev);
|
||||||
virtio_fs_cleanup_vqs(vdev, fs);
|
virtio_fs_cleanup_vqs(vdev, fs);
|
||||||
|
kfree(fs->vqs);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
vdev->priv = NULL;
|
vdev->priv = NULL;
|
||||||
|
@ -1413,9 +1415,10 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
|
||||||
{
|
{
|
||||||
struct virtio_fs *fs;
|
struct virtio_fs *fs;
|
||||||
struct super_block *sb;
|
struct super_block *sb;
|
||||||
struct fuse_conn *fc;
|
struct fuse_conn *fc = NULL;
|
||||||
struct fuse_mount *fm;
|
struct fuse_mount *fm;
|
||||||
int err;
|
unsigned int virtqueue_size;
|
||||||
|
int err = -EIO;
|
||||||
|
|
||||||
/* This gets a reference on virtio_fs object. This ptr gets installed
|
/* This gets a reference on virtio_fs object. This ptr gets installed
|
||||||
* in fc->iq->priv. Once fuse_conn is going away, it calls ->put()
|
* in fc->iq->priv. Once fuse_conn is going away, it calls ->put()
|
||||||
|
@ -1427,6 +1430,10 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtqueue_size = virtqueue_get_vring_size(fs->vqs[VQ_REQUEST].vq);
|
||||||
|
if (WARN_ON(virtqueue_size <= FUSE_HEADER_OVERHEAD))
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
fc = kzalloc(sizeof(struct fuse_conn), GFP_KERNEL);
|
fc = kzalloc(sizeof(struct fuse_conn), GFP_KERNEL);
|
||||||
if (!fc)
|
if (!fc)
|
||||||
|
@ -1436,12 +1443,15 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
|
||||||
if (!fm)
|
if (!fm)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
fuse_conn_init(fc, fm, get_user_ns(current_user_ns()),
|
fuse_conn_init(fc, fm, fsc->user_ns, &virtio_fs_fiq_ops, fs);
|
||||||
&virtio_fs_fiq_ops, fs);
|
|
||||||
fc->release = fuse_free_conn;
|
fc->release = fuse_free_conn;
|
||||||
fc->delete_stale = true;
|
fc->delete_stale = true;
|
||||||
fc->auto_submounts = true;
|
fc->auto_submounts = true;
|
||||||
|
|
||||||
|
/* Tell FUSE to split requests that exceed the virtqueue's size */
|
||||||
|
fc->max_pages_limit = min_t(unsigned int, fc->max_pages_limit,
|
||||||
|
virtqueue_size - FUSE_HEADER_OVERHEAD);
|
||||||
|
|
||||||
fsc->s_fs_info = fm;
|
fsc->s_fs_info = fm;
|
||||||
sb = sget_fc(fsc, virtio_fs_test_super, set_anon_super_fc);
|
sb = sget_fc(fsc, virtio_fs_test_super, set_anon_super_fc);
|
||||||
if (fsc->s_fs_info) {
|
if (fsc->s_fs_info) {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include <linux/posix_acl_xattr.h>
|
#include <linux/posix_acl_xattr.h>
|
||||||
|
|
||||||
int fuse_setxattr(struct inode *inode, const char *name, const void *value,
|
int fuse_setxattr(struct inode *inode, const char *name, const void *value,
|
||||||
size_t size, int flags)
|
size_t size, int flags, unsigned int extra_flags)
|
||||||
{
|
{
|
||||||
struct fuse_mount *fm = get_fuse_mount(inode);
|
struct fuse_mount *fm = get_fuse_mount(inode);
|
||||||
FUSE_ARGS(args);
|
FUSE_ARGS(args);
|
||||||
|
@ -25,10 +25,13 @@ int fuse_setxattr(struct inode *inode, const char *name, const void *value,
|
||||||
memset(&inarg, 0, sizeof(inarg));
|
memset(&inarg, 0, sizeof(inarg));
|
||||||
inarg.size = size;
|
inarg.size = size;
|
||||||
inarg.flags = flags;
|
inarg.flags = flags;
|
||||||
|
inarg.setxattr_flags = extra_flags;
|
||||||
|
|
||||||
args.opcode = FUSE_SETXATTR;
|
args.opcode = FUSE_SETXATTR;
|
||||||
args.nodeid = get_node_id(inode);
|
args.nodeid = get_node_id(inode);
|
||||||
args.in_numargs = 3;
|
args.in_numargs = 3;
|
||||||
args.in_args[0].size = sizeof(inarg);
|
args.in_args[0].size = fm->fc->setxattr_ext ?
|
||||||
|
sizeof(inarg) : FUSE_COMPAT_SETXATTR_IN_SIZE;
|
||||||
args.in_args[0].value = &inarg;
|
args.in_args[0].value = &inarg;
|
||||||
args.in_args[1].size = strlen(name) + 1;
|
args.in_args[1].size = strlen(name) + 1;
|
||||||
args.in_args[1].value = name;
|
args.in_args[1].value = name;
|
||||||
|
@ -199,7 +202,7 @@ static int fuse_xattr_set(const struct xattr_handler *handler,
|
||||||
if (!value)
|
if (!value)
|
||||||
return fuse_removexattr(inode, name);
|
return fuse_removexattr(inode, name);
|
||||||
|
|
||||||
return fuse_setxattr(inode, name, value, size, flags);
|
return fuse_setxattr(inode, name, value, size, flags, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool no_xattr_list(struct dentry *dentry)
|
static bool no_xattr_list(struct dentry *dentry)
|
||||||
|
|
|
@ -179,6 +179,8 @@
|
||||||
* 7.33
|
* 7.33
|
||||||
* - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
|
* - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID
|
||||||
* - add FUSE_OPEN_KILL_SUIDGID
|
* - add FUSE_OPEN_KILL_SUIDGID
|
||||||
|
* - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT
|
||||||
|
* - add FUSE_SETXATTR_ACL_KILL_SGID
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _LINUX_FUSE_H
|
#ifndef _LINUX_FUSE_H
|
||||||
|
@ -330,6 +332,7 @@ struct fuse_file_lock {
|
||||||
* does not have CAP_FSETID. Additionally upon
|
* does not have CAP_FSETID. Additionally upon
|
||||||
* write/truncate sgid is killed only if file has group
|
* write/truncate sgid is killed only if file has group
|
||||||
* execute permission. (Same as Linux VFS behavior).
|
* execute permission. (Same as Linux VFS behavior).
|
||||||
|
* FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in
|
||||||
*/
|
*/
|
||||||
#define FUSE_ASYNC_READ (1 << 0)
|
#define FUSE_ASYNC_READ (1 << 0)
|
||||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||||
|
@ -360,6 +363,7 @@ struct fuse_file_lock {
|
||||||
#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)
|
#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28)
|
||||||
|
#define FUSE_SETXATTR_EXT (1 << 29)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CUSE INIT request/reply flags
|
* CUSE INIT request/reply flags
|
||||||
|
@ -451,6 +455,12 @@ struct fuse_file_lock {
|
||||||
*/
|
*/
|
||||||
#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
|
#define FUSE_OPEN_KILL_SUIDGID (1 << 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setxattr flags
|
||||||
|
* FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set
|
||||||
|
*/
|
||||||
|
#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0)
|
||||||
|
|
||||||
enum fuse_opcode {
|
enum fuse_opcode {
|
||||||
FUSE_LOOKUP = 1,
|
FUSE_LOOKUP = 1,
|
||||||
FUSE_FORGET = 2, /* no reply */
|
FUSE_FORGET = 2, /* no reply */
|
||||||
|
@ -681,9 +691,13 @@ struct fuse_fsync_in {
|
||||||
uint32_t padding;
|
uint32_t padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define FUSE_COMPAT_SETXATTR_IN_SIZE 8
|
||||||
|
|
||||||
struct fuse_setxattr_in {
|
struct fuse_setxattr_in {
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
uint32_t setxattr_flags;
|
||||||
|
uint32_t padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_getxattr_in {
|
struct fuse_getxattr_in {
|
||||||
|
|
Loading…
Reference in New Issue