}
EXPORT_SYMBOL_GPL(fuse_dev_alloc);
-static void fuse_dev_install_with_pq(struct fuse_dev *fud, struct fuse_chan *fch,
+/*
+ * Installs @fch into @fud, return true on success. "Consumes" @pq in either case.
+ */
+static bool fuse_dev_install_with_pq(struct fuse_dev *fud, struct fuse_chan *fch,
struct list_head *pq)
{
struct fuse_chan *old_fch;
- spin_lock(&fch->lock);
+ guard(spinlock)(&fch->lock);
/*
* Pairs with:
* - xchg() in fuse_dev_release()
* - it was already set to a different fc
* - it was set to disconneted
*/
- fch->connected = 0;
kfree(pq);
- } else {
- if (pq) {
- WARN_ON(fud->pq.processing);
- fud->pq.processing = pq;
- }
- list_add_tail(&fud->entry, &fch->devices);
- fuse_conn_get(fch->conn);
- wake_up_all(&fuse_dev_waitq);
+ return false;
}
- spin_unlock(&fch->lock);
+ if (pq) {
+ WARN_ON(fud->pq.processing);
+ fud->pq.processing = pq;
+ }
+ list_add_tail(&fud->entry, &fch->devices);
+ fuse_conn_get(fch->conn);
+ wake_up_all(&fuse_dev_waitq);
+ return true;
}
void fuse_dev_install(struct fuse_dev *fud, struct fuse_chan *fch)
struct list_head *pq = fch->pq_prealloc;
fch->pq_prealloc = NULL;
- fuse_dev_install_with_pq(fud, fch, pq);
+ if (!fuse_dev_install_with_pq(fud, fch, pq)) {
+ /* Channel is not usable without a dev */
+ fuse_chan_abort(fch, false);
+ }
}
EXPORT_SYMBOL_GPL(fuse_dev_install);
if (IS_ERR(fud))
return PTR_ERR(fud);
- new_fud = fuse_file_to_fud(file);
- if (fuse_dev_chan_get(new_fud))
- return -EINVAL;
-
pq = fuse_pqueue_alloc();
if (!pq)
return -ENOMEM;
- fuse_dev_install_with_pq(new_fud, fud->chan, pq);
+ new_fud = fuse_file_to_fud(file);
+ if (!fuse_dev_install_with_pq(new_fud, fud->chan, pq))
+ return -EINVAL;
return 0;
}