mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 21:24:08 +08:00
fuse update for 5.5
-----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCXedyjQAKCRDh3BK/laaZ PCR7AQCf+bb3/so1bygFeblBTT4UZbYZRXz2nZNSA5tgJvafWwD+I4MlqR+tEixb gZEusbtAVrtm3hJrBc+1fA1wacGhmAg= =Jg/0 -----END PGP SIGNATURE----- Merge tag 'fuse-update-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse Pull fuse update from Miklos Szeredi: - Fix a regression introduced in the last release - Fix a number of issues with validating data coming from userspace - Some cleanups in virtiofs * tag 'fuse-update-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: fuse: fix Kconfig indentation fuse: fix leak of fuse_io_priv virtiofs: Use completions while waiting for queue to be drained virtiofs: Do not send forget request "struct list_head" element virtiofs: Use a common function to send forget virtiofs: Fix old-style declaration fuse: verify nlink fuse: verify write return fuse: verify attributes
This commit is contained in:
commit
7ce4fab819
@ -248,7 +248,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
||||
kfree(forget);
|
||||
if (ret == -ENOMEM)
|
||||
goto out;
|
||||
if (ret || (outarg.attr.mode ^ inode->i_mode) & S_IFMT)
|
||||
if (ret || fuse_invalid_attr(&outarg.attr) ||
|
||||
(outarg.attr.mode ^ inode->i_mode) & S_IFMT)
|
||||
goto invalid;
|
||||
|
||||
forget_all_cached_acls(inode);
|
||||
@ -319,6 +320,12 @@ int fuse_valid_type(int m)
|
||||
S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m);
|
||||
}
|
||||
|
||||
bool fuse_invalid_attr(struct fuse_attr *attr)
|
||||
{
|
||||
return !fuse_valid_type(attr->mode) ||
|
||||
attr->size > LLONG_MAX;
|
||||
}
|
||||
|
||||
int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name,
|
||||
struct fuse_entry_out *outarg, struct inode **inode)
|
||||
{
|
||||
@ -350,7 +357,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
|
||||
err = -EIO;
|
||||
if (!outarg->nodeid)
|
||||
goto out_put_forget;
|
||||
if (!fuse_valid_type(outarg->attr.mode))
|
||||
if (fuse_invalid_attr(&outarg->attr))
|
||||
goto out_put_forget;
|
||||
|
||||
*inode = fuse_iget(sb, outarg->nodeid, outarg->generation,
|
||||
@ -475,7 +482,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
|
||||
goto out_free_ff;
|
||||
|
||||
err = -EIO;
|
||||
if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid))
|
||||
if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid) ||
|
||||
fuse_invalid_attr(&outentry.attr))
|
||||
goto out_free_ff;
|
||||
|
||||
ff->fh = outopen.fh;
|
||||
@ -583,7 +591,7 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_args *args,
|
||||
goto out_put_forget_req;
|
||||
|
||||
err = -EIO;
|
||||
if (invalid_nodeid(outarg.nodeid))
|
||||
if (invalid_nodeid(outarg.nodeid) || fuse_invalid_attr(&outarg.attr))
|
||||
goto out_put_forget_req;
|
||||
|
||||
if ((outarg.attr.mode ^ mode) & S_IFMT)
|
||||
@ -862,6 +870,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
|
||||
|
||||
spin_lock(&fi->lock);
|
||||
fi->attr_version = atomic64_inc_return(&fc->attr_version);
|
||||
if (likely(inode->i_nlink < UINT_MAX))
|
||||
inc_nlink(inode);
|
||||
spin_unlock(&fi->lock);
|
||||
fuse_invalidate_attr(inode);
|
||||
@ -942,7 +951,8 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
|
||||
args.out_args[0].value = &outarg;
|
||||
err = fuse_simple_request(fc, &args);
|
||||
if (!err) {
|
||||
if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
||||
if (fuse_invalid_attr(&outarg.attr) ||
|
||||
(inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
||||
make_bad_inode(inode);
|
||||
err = -EIO;
|
||||
} else {
|
||||
@ -1563,7 +1573,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
||||
if (fuse_invalid_attr(&outarg.attr) ||
|
||||
(inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
|
||||
make_bad_inode(inode);
|
||||
err = -EIO;
|
||||
goto error;
|
||||
|
@ -713,8 +713,10 @@ static ssize_t fuse_async_req_send(struct fuse_conn *fc,
|
||||
|
||||
ia->ap.args.end = fuse_aio_complete_req;
|
||||
err = fuse_simple_background(fc, &ia->ap.args, GFP_KERNEL);
|
||||
if (err)
|
||||
fuse_aio_complete_req(fc, &ia->ap.args, err);
|
||||
|
||||
return err ?: num_bytes;
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
static ssize_t fuse_send_read(struct fuse_io_args *ia, loff_t pos, size_t count,
|
||||
@ -1096,6 +1098,8 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
|
||||
ia->write.in.flags = fuse_write_flags(iocb);
|
||||
|
||||
err = fuse_simple_request(fc, &ap->args);
|
||||
if (!err && ia->write.out.size > count)
|
||||
err = -EIO;
|
||||
|
||||
offset = ap->descs[0].offset;
|
||||
count = ia->write.out.size;
|
||||
|
@ -989,6 +989,8 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc);
|
||||
*/
|
||||
int fuse_valid_type(int m);
|
||||
|
||||
bool fuse_invalid_attr(struct fuse_attr *attr);
|
||||
|
||||
/**
|
||||
* Is current process allowed to perform filesystem operation?
|
||||
*/
|
||||
|
@ -184,7 +184,7 @@ static int fuse_direntplus_link(struct file *file,
|
||||
|
||||
if (invalid_nodeid(o->nodeid))
|
||||
return -EIO;
|
||||
if (!fuse_valid_type(o->attr.mode))
|
||||
if (fuse_invalid_attr(&o->attr))
|
||||
return -EIO;
|
||||
|
||||
fc = get_fuse_conn(dir);
|
||||
|
@ -35,6 +35,7 @@ struct virtio_fs_vq {
|
||||
struct fuse_dev *fud;
|
||||
bool connected;
|
||||
long in_flight;
|
||||
struct completion in_flight_zero; /* No inflight requests */
|
||||
char name[24];
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
@ -48,11 +49,15 @@ struct virtio_fs {
|
||||
unsigned int num_request_queues; /* number of request queues */
|
||||
};
|
||||
|
||||
struct virtio_fs_forget {
|
||||
struct virtio_fs_forget_req {
|
||||
struct fuse_in_header ih;
|
||||
struct fuse_forget_in arg;
|
||||
};
|
||||
|
||||
struct virtio_fs_forget {
|
||||
/* This request can be temporarily queued on virt queue */
|
||||
struct list_head list;
|
||||
struct virtio_fs_forget_req req;
|
||||
};
|
||||
|
||||
static int virtio_fs_enqueue_req(struct virtio_fs_vq *fsvq,
|
||||
@ -81,6 +86,8 @@ static inline void dec_in_flight_req(struct virtio_fs_vq *fsvq)
|
||||
{
|
||||
WARN_ON(fsvq->in_flight <= 0);
|
||||
fsvq->in_flight--;
|
||||
if (!fsvq->in_flight)
|
||||
complete(&fsvq->in_flight_zero);
|
||||
}
|
||||
|
||||
static void release_virtio_fs_obj(struct kref *ref)
|
||||
@ -111,22 +118,23 @@ static void virtio_fs_drain_queue(struct virtio_fs_vq *fsvq)
|
||||
WARN_ON(fsvq->in_flight < 0);
|
||||
|
||||
/* Wait for in flight requests to finish.*/
|
||||
while (1) {
|
||||
spin_lock(&fsvq->lock);
|
||||
if (!fsvq->in_flight) {
|
||||
if (fsvq->in_flight) {
|
||||
/* We are holding virtio_fs_mutex. There should not be any
|
||||
* waiters waiting for completion.
|
||||
*/
|
||||
reinit_completion(&fsvq->in_flight_zero);
|
||||
spin_unlock(&fsvq->lock);
|
||||
break;
|
||||
}
|
||||
wait_for_completion(&fsvq->in_flight_zero);
|
||||
} else {
|
||||
spin_unlock(&fsvq->lock);
|
||||
/* TODO use completion instead of timeout */
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
flush_work(&fsvq->done_work);
|
||||
flush_delayed_work(&fsvq->dispatch_work);
|
||||
}
|
||||
|
||||
static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
|
||||
static void virtio_fs_drain_all_queues_locked(struct virtio_fs *fs)
|
||||
{
|
||||
struct virtio_fs_vq *fsvq;
|
||||
int i;
|
||||
@ -137,6 +145,19 @@ static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_fs_drain_all_queues(struct virtio_fs *fs)
|
||||
{
|
||||
/* Provides mutual exclusion between ->remove and ->kill_sb
|
||||
* paths. We don't want both of these draining queue at the
|
||||
* same time. Current completion logic reinits completion
|
||||
* and that means there should not be any other thread
|
||||
* doing reinit or waiting for completion already.
|
||||
*/
|
||||
mutex_lock(&virtio_fs_mutex);
|
||||
virtio_fs_drain_all_queues_locked(fs);
|
||||
mutex_unlock(&virtio_fs_mutex);
|
||||
}
|
||||
|
||||
static void virtio_fs_start_all_queues(struct virtio_fs *fs)
|
||||
{
|
||||
struct virtio_fs_vq *fsvq;
|
||||
@ -313,17 +334,72 @@ static void virtio_fs_request_dispatch_work(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 1 if queue is full and sender should wait a bit before sending
|
||||
* next request, 0 otherwise.
|
||||
*/
|
||||
static int send_forget_request(struct virtio_fs_vq *fsvq,
|
||||
struct virtio_fs_forget *forget,
|
||||
bool in_flight)
|
||||
{
|
||||
struct scatterlist sg;
|
||||
struct virtqueue *vq;
|
||||
int ret = 0;
|
||||
bool notify;
|
||||
struct virtio_fs_forget_req *req = &forget->req;
|
||||
|
||||
spin_lock(&fsvq->lock);
|
||||
if (!fsvq->connected) {
|
||||
if (in_flight)
|
||||
dec_in_flight_req(fsvq);
|
||||
kfree(forget);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sg_init_one(&sg, req, sizeof(*req));
|
||||
vq = fsvq->vq;
|
||||
dev_dbg(&vq->vdev->dev, "%s\n", __func__);
|
||||
|
||||
ret = virtqueue_add_outbuf(vq, &sg, 1, forget, GFP_ATOMIC);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOMEM || ret == -ENOSPC) {
|
||||
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later\n",
|
||||
ret);
|
||||
list_add_tail(&forget->list, &fsvq->queued_reqs);
|
||||
schedule_delayed_work(&fsvq->dispatch_work,
|
||||
msecs_to_jiffies(1));
|
||||
if (!in_flight)
|
||||
inc_in_flight_req(fsvq);
|
||||
/* Queue is full */
|
||||
ret = 1;
|
||||
} else {
|
||||
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
|
||||
ret);
|
||||
kfree(forget);
|
||||
if (in_flight)
|
||||
dec_in_flight_req(fsvq);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!in_flight)
|
||||
inc_in_flight_req(fsvq);
|
||||
notify = virtqueue_kick_prepare(vq);
|
||||
spin_unlock(&fsvq->lock);
|
||||
|
||||
if (notify)
|
||||
virtqueue_notify(vq);
|
||||
return ret;
|
||||
out:
|
||||
spin_unlock(&fsvq->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
|
||||
{
|
||||
struct virtio_fs_forget *forget;
|
||||
struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
|
||||
dispatch_work.work);
|
||||
struct virtqueue *vq = fsvq->vq;
|
||||
struct scatterlist sg;
|
||||
struct scatterlist *sgs[] = {&sg};
|
||||
bool notify;
|
||||
int ret;
|
||||
|
||||
pr_debug("virtio-fs: worker %s called.\n", __func__);
|
||||
while (1) {
|
||||
spin_lock(&fsvq->lock);
|
||||
@ -335,44 +411,10 @@ static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
|
||||
}
|
||||
|
||||
list_del(&forget->list);
|
||||
if (!fsvq->connected) {
|
||||
dec_in_flight_req(fsvq);
|
||||
spin_unlock(&fsvq->lock);
|
||||
kfree(forget);
|
||||
continue;
|
||||
}
|
||||
|
||||
sg_init_one(&sg, forget, sizeof(*forget));
|
||||
|
||||
/* Enqueue the request */
|
||||
dev_dbg(&vq->vdev->dev, "%s\n", __func__);
|
||||
ret = virtqueue_add_sgs(vq, sgs, 1, 0, forget, GFP_ATOMIC);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOMEM || ret == -ENOSPC) {
|
||||
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later\n",
|
||||
ret);
|
||||
list_add_tail(&forget->list,
|
||||
&fsvq->queued_reqs);
|
||||
schedule_delayed_work(&fsvq->dispatch_work,
|
||||
msecs_to_jiffies(1));
|
||||
} else {
|
||||
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
|
||||
ret);
|
||||
dec_in_flight_req(fsvq);
|
||||
kfree(forget);
|
||||
}
|
||||
spin_unlock(&fsvq->lock);
|
||||
if (send_forget_request(fsvq, forget, true))
|
||||
return;
|
||||
}
|
||||
|
||||
notify = virtqueue_kick_prepare(vq);
|
||||
spin_unlock(&fsvq->lock);
|
||||
|
||||
if (notify)
|
||||
virtqueue_notify(vq);
|
||||
pr_debug("virtio-fs: worker %s dispatched one forget request.\n",
|
||||
__func__);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate and copy args into req->argbuf */
|
||||
@ -556,6 +598,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
|
||||
INIT_LIST_HEAD(&fs->vqs[VQ_HIPRIO].end_reqs);
|
||||
INIT_DELAYED_WORK(&fs->vqs[VQ_HIPRIO].dispatch_work,
|
||||
virtio_fs_hiprio_dispatch_work);
|
||||
init_completion(&fs->vqs[VQ_HIPRIO].in_flight_zero);
|
||||
spin_lock_init(&fs->vqs[VQ_HIPRIO].lock);
|
||||
|
||||
/* Initialize the requests virtqueues */
|
||||
@ -566,6 +609,7 @@ static int virtio_fs_setup_vqs(struct virtio_device *vdev,
|
||||
virtio_fs_request_dispatch_work);
|
||||
INIT_LIST_HEAD(&fs->vqs[i].queued_reqs);
|
||||
INIT_LIST_HEAD(&fs->vqs[i].end_reqs);
|
||||
init_completion(&fs->vqs[i].in_flight_zero);
|
||||
snprintf(fs->vqs[i].name, sizeof(fs->vqs[i].name),
|
||||
"requests.%u", i - VQ_REQUEST);
|
||||
callbacks[i] = virtio_fs_vq_done;
|
||||
@ -659,7 +703,7 @@ static void virtio_fs_remove(struct virtio_device *vdev)
|
||||
/* This device is going away. No one should get new reference */
|
||||
list_del_init(&fs->list);
|
||||
virtio_fs_stop_all_queues(fs);
|
||||
virtio_fs_drain_all_queues(fs);
|
||||
virtio_fs_drain_all_queues_locked(fs);
|
||||
vdev->config->reset(vdev);
|
||||
virtio_fs_cleanup_vqs(vdev, fs);
|
||||
|
||||
@ -684,12 +728,12 @@ static int virtio_fs_restore(struct virtio_device *vdev)
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
const static struct virtio_device_id id_table[] = {
|
||||
static const struct virtio_device_id id_table[] = {
|
||||
{ VIRTIO_ID_FS, VIRTIO_DEV_ANY_ID },
|
||||
{},
|
||||
};
|
||||
|
||||
const static unsigned int feature_table[] = {};
|
||||
static const unsigned int feature_table[] = {};
|
||||
|
||||
static struct virtio_driver virtio_fs_driver = {
|
||||
.driver.name = KBUILD_MODNAME,
|
||||
@ -710,14 +754,10 @@ __releases(fiq->lock)
|
||||
{
|
||||
struct fuse_forget_link *link;
|
||||
struct virtio_fs_forget *forget;
|
||||
struct scatterlist sg;
|
||||
struct scatterlist *sgs[] = {&sg};
|
||||
struct virtio_fs_forget_req *req;
|
||||
struct virtio_fs *fs;
|
||||
struct virtqueue *vq;
|
||||
struct virtio_fs_vq *fsvq;
|
||||
bool notify;
|
||||
u64 unique;
|
||||
int ret;
|
||||
|
||||
link = fuse_dequeue_forget(fiq, 1, NULL);
|
||||
unique = fuse_get_unique(fiq);
|
||||
@ -728,57 +768,19 @@ __releases(fiq->lock)
|
||||
|
||||
/* Allocate a buffer for the request */
|
||||
forget = kmalloc(sizeof(*forget), GFP_NOFS | __GFP_NOFAIL);
|
||||
req = &forget->req;
|
||||
|
||||
forget->ih = (struct fuse_in_header){
|
||||
req->ih = (struct fuse_in_header){
|
||||
.opcode = FUSE_FORGET,
|
||||
.nodeid = link->forget_one.nodeid,
|
||||
.unique = unique,
|
||||
.len = sizeof(*forget),
|
||||
.len = sizeof(*req),
|
||||
};
|
||||
forget->arg = (struct fuse_forget_in){
|
||||
req->arg = (struct fuse_forget_in){
|
||||
.nlookup = link->forget_one.nlookup,
|
||||
};
|
||||
|
||||
sg_init_one(&sg, forget, sizeof(*forget));
|
||||
|
||||
/* Enqueue the request */
|
||||
spin_lock(&fsvq->lock);
|
||||
|
||||
if (!fsvq->connected) {
|
||||
kfree(forget);
|
||||
spin_unlock(&fsvq->lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
vq = fsvq->vq;
|
||||
dev_dbg(&vq->vdev->dev, "%s\n", __func__);
|
||||
|
||||
ret = virtqueue_add_sgs(vq, sgs, 1, 0, forget, GFP_ATOMIC);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOMEM || ret == -ENOSPC) {
|
||||
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Will try later.\n",
|
||||
ret);
|
||||
list_add_tail(&forget->list, &fsvq->queued_reqs);
|
||||
schedule_delayed_work(&fsvq->dispatch_work,
|
||||
msecs_to_jiffies(1));
|
||||
inc_in_flight_req(fsvq);
|
||||
} else {
|
||||
pr_debug("virtio-fs: Could not queue FORGET: err=%d. Dropping it.\n",
|
||||
ret);
|
||||
kfree(forget);
|
||||
}
|
||||
spin_unlock(&fsvq->lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
inc_in_flight_req(fsvq);
|
||||
notify = virtqueue_kick_prepare(vq);
|
||||
|
||||
spin_unlock(&fsvq->lock);
|
||||
|
||||
if (notify)
|
||||
virtqueue_notify(vq);
|
||||
out:
|
||||
send_forget_request(fsvq, forget, false);
|
||||
kfree(link);
|
||||
}
|
||||
|
||||
@ -1026,7 +1028,7 @@ __releases(fiq->lock)
|
||||
}
|
||||
}
|
||||
|
||||
const static struct fuse_iqueue_ops virtio_fs_fiq_ops = {
|
||||
static const struct fuse_iqueue_ops virtio_fs_fiq_ops = {
|
||||
.wake_forget_and_unlock = virtio_fs_wake_forget_and_unlock,
|
||||
.wake_interrupt_and_unlock = virtio_fs_wake_interrupt_and_unlock,
|
||||
.wake_pending_and_unlock = virtio_fs_wake_pending_and_unlock,
|
||||
|
Loading…
Reference in New Issue
Block a user