]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fuse: create notify.c
authorMiklos Szeredi <mszeredi@redhat.com>
Tue, 31 Mar 2026 12:50:24 +0000 (14:50 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Mon, 15 Jun 2026 12:06:17 +0000 (14:06 +0200)
Move FUSE_NOTIFY_* handling into a separate source file.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/Makefile
fs/fuse/dev.c
fs/fuse/dev.h
fs/fuse/fuse_dev_i.h

index 26086bf5b49426eb391555d44d4332166c8e923b..245e67852b03e8c8e76a23c86ae75e2c0ac185b3 100644 (file)
@@ -12,7 +12,7 @@ obj-$(CONFIG_VIRTIO_FS) += virtiofs.o
 
 fuse-y := trace.o      # put trace.o first so we see ftrace errors sooner
 fuse-y += dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o ioctl.o req_timeout.o req.o
-fuse-y += poll.o
+fuse-y += poll.o notify.o
 fuse-y += iomode.o
 fuse-$(CONFIG_FUSE_DAX) += dax.o
 fuse-$(CONFIG_FUSE_PASSTHROUGH) += passthrough.o backing.o
index 18a9c4f3d63c756e1db4b453b9ecaa7e4d0c7d72..41f6a1b9397f5fb3044397d7f7b08c9058f78f22 100644 (file)
@@ -1200,8 +1200,8 @@ static int fuse_ref_folio(struct fuse_copy_state *cs, struct folio *folio,
  * Copy a folio in the request to/from the userspace buffer.  Must be
  * done atomically
  */
-static int fuse_copy_folio(struct fuse_copy_state *cs, struct folio **foliop,
-                          unsigned offset, unsigned count, int zeroing)
+int fuse_copy_folio(struct fuse_copy_state *cs, struct folio **foliop,
+                   unsigned offset, unsigned count, int zeroing)
 {
        int err;
        struct folio *folio = *foliop;
@@ -1282,7 +1282,7 @@ static int fuse_copy_folios(struct fuse_copy_state *cs, unsigned nbytes,
 }
 
 /* Copy a single argument in the request to/from userspace buffer */
-static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size)
+int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size)
 {
        while (size) {
                if (!cs->len) {
@@ -1720,343 +1720,6 @@ out:
        return ret;
 }
 
-static int fuse_notify_poll(struct fuse_conn *fc, unsigned int size,
-                           struct fuse_copy_state *cs)
-{
-       struct fuse_notify_poll_wakeup_out outarg;
-       int err;
-
-       if (size != sizeof(outarg))
-               return -EINVAL;
-
-       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
-       if (err)
-               return err;
-
-       fuse_copy_finish(cs);
-       return fuse_notify_poll_wakeup(fc, &outarg);
-}
-
-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;
-
-       if (size != sizeof(outarg))
-               return -EINVAL;
-
-       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
-       if (err)
-               return err;
-       fuse_copy_finish(cs);
-
-       down_read(&fc->killsb);
-       err = fuse_reverse_inval_inode(fc, outarg.ino,
-                                      outarg.off, outarg.len);
-       up_read(&fc->killsb);
-       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;
-       char *buf;
-       struct qstr name;
-
-       if (size < sizeof(outarg))
-               return -EINVAL;
-
-       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
-       if (err)
-               return err;
-
-       if (outarg.namelen > fc->name_max)
-               return -ENAMETOOLONG;
-
-       err = -EINVAL;
-       if (size != sizeof(outarg) + outarg.namelen + 1)
-               return -EINVAL;
-
-       buf = kzalloc(outarg.namelen + 1, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       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;
-
-       down_read(&fc->killsb);
-       err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name, outarg.flags);
-       up_read(&fc->killsb);
-err:
-       kfree(buf);
-       return err;
-}
-
-static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
-                             struct fuse_copy_state *cs)
-{
-       struct fuse_notify_delete_out outarg;
-       int err;
-       char *buf;
-       struct qstr name;
-
-       if (size < sizeof(outarg))
-               return -EINVAL;
-
-       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
-       if (err)
-               return err;
-
-       if (outarg.namelen > fc->name_max)
-               return -ENAMETOOLONG;
-
-       if (size != sizeof(outarg) + outarg.namelen + 1)
-               return -EINVAL;
-
-       buf = kzalloc(outarg.namelen + 1, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       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;
-
-       down_read(&fc->killsb);
-       err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name, 0);
-       up_read(&fc->killsb);
-err:
-       kfree(buf);
-       return err;
-}
-
-static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,
-                            struct fuse_copy_state *cs)
-{
-       struct fuse_notify_store_out outarg;
-       struct inode *inode;
-       struct address_space *mapping;
-       u64 nodeid;
-       int err;
-       unsigned int num;
-       loff_t file_size;
-       loff_t pos;
-       loff_t end;
-
-       if (size < sizeof(outarg))
-               return -EINVAL;
-
-       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
-       if (err)
-               return err;
-
-       if (size - sizeof(outarg) != outarg.size)
-               return -EINVAL;
-
-       if (outarg.offset >= MAX_LFS_FILESIZE)
-               return -EINVAL;
-
-       nodeid = outarg.nodeid;
-       pos = outarg.offset;
-       num = min(outarg.size, MAX_LFS_FILESIZE - pos);
-
-       down_read(&fc->killsb);
-
-       err = -ENOENT;
-       inode = fuse_ilookup(fc, nodeid,  NULL);
-       if (!inode)
-               goto out_up_killsb;
-
-       mapping = inode->i_mapping;
-       file_size = i_size_read(inode);
-       end = pos + num;
-       if (end > file_size) {
-               file_size = end;
-               fuse_write_update_attr(inode, file_size, num);
-       }
-
-       while (num) {
-               struct folio *folio;
-               unsigned int folio_offset;
-               unsigned int nr_bytes;
-               pgoff_t index = pos >> PAGE_SHIFT;
-
-               folio = filemap_grab_folio(mapping, index);
-               err = PTR_ERR(folio);
-               if (IS_ERR(folio))
-                       goto out_iput;
-
-               folio_offset = offset_in_folio(folio, pos);
-               nr_bytes = min(num, folio_size(folio) - folio_offset);
-
-               err = fuse_copy_folio(cs, &folio, folio_offset, nr_bytes, 0);
-               if (!folio_test_uptodate(folio) && !err && folio_offset == 0 &&
-                   (nr_bytes == folio_size(folio) || file_size == end)) {
-                       folio_zero_segment(folio, nr_bytes, folio_size(folio));
-                       folio_mark_uptodate(folio);
-               }
-               folio_unlock(folio);
-               folio_put(folio);
-
-               if (err)
-                       goto out_iput;
-
-               pos += nr_bytes;
-               num -= nr_bytes;
-       }
-
-       err = 0;
-
-out_iput:
-       iput(inode);
-out_up_killsb:
-       up_read(&fc->killsb);
-       return err;
-}
-
-struct fuse_retrieve_args {
-       struct fuse_args_pages ap;
-       struct fuse_notify_retrieve_in inarg;
-};
-
-static void fuse_retrieve_end(struct fuse_args *args, int error)
-{
-       struct fuse_retrieve_args *ra =
-               container_of(args, typeof(*ra), ap.args);
-
-       release_pages(ra->ap.folios, ra->ap.num_folios);
-       kfree(ra);
-}
-
-static int fuse_retrieve(struct fuse_mount *fm, struct inode *inode,
-                        struct fuse_notify_retrieve_out *outarg)
-{
-       int err;
-       struct address_space *mapping = inode->i_mapping;
-       loff_t file_size;
-       unsigned int num;
-       unsigned int offset;
-       size_t total_len = 0;
-       unsigned int num_pages;
-       struct fuse_conn *fc = fm->fc;
-       struct fuse_retrieve_args *ra;
-       size_t args_size = sizeof(*ra);
-       struct fuse_args_pages *ap;
-       struct fuse_args *args;
-       loff_t pos = outarg->offset;
-
-       offset = offset_in_page(pos);
-       file_size = i_size_read(inode);
-
-       num = min(outarg->size, fc->max_write);
-       if (pos > file_size)
-               num = 0;
-       else if (num > file_size - pos)
-               num = file_size - pos;
-
-       num_pages = DIV_ROUND_UP(num + offset, PAGE_SIZE);
-       num_pages = min(num_pages, fc->max_pages);
-       num = min(num, num_pages << PAGE_SHIFT);
-
-       args_size += num_pages * (sizeof(ap->folios[0]) + sizeof(ap->descs[0]));
-
-       ra = kzalloc(args_size, GFP_KERNEL);
-       if (!ra)
-               return -ENOMEM;
-
-       ap = &ra->ap;
-       ap->folios = (void *) (ra + 1);
-       ap->descs = (void *) (ap->folios + num_pages);
-
-       args = &ap->args;
-       args->nodeid = outarg->nodeid;
-       args->opcode = FUSE_NOTIFY_REPLY;
-       args->in_numargs = 3;
-       args->in_pages = true;
-       args->end = fuse_retrieve_end;
-
-       while (num && ap->num_folios < num_pages) {
-               struct folio *folio;
-               unsigned int folio_offset;
-               unsigned int nr_bytes;
-               pgoff_t index = pos >> PAGE_SHIFT;
-
-               folio = filemap_get_folio(mapping, index);
-               if (IS_ERR(folio))
-                       break;
-
-               folio_offset = offset_in_folio(folio, pos);
-               nr_bytes = min(folio_size(folio) - folio_offset, num);
-
-               ap->folios[ap->num_folios] = folio;
-               ap->descs[ap->num_folios].offset = folio_offset;
-               ap->descs[ap->num_folios].length = nr_bytes;
-               ap->num_folios++;
-
-               pos += nr_bytes;
-               num -= nr_bytes;
-               total_len += nr_bytes;
-       }
-       ra->inarg.offset = outarg->offset;
-       ra->inarg.size = total_len;
-       fuse_set_zero_arg0(args);
-       args->in_args[1].size = sizeof(ra->inarg);
-       args->in_args[1].value = &ra->inarg;
-       args->in_args[2].size = total_len;
-
-       err = fuse_simple_notify_reply(fm, args, outarg->notify_unique);
-       if (err)
-               fuse_retrieve_end(args, err);
-
-       return err;
-}
-
-static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
-                               struct fuse_copy_state *cs)
-{
-       struct fuse_notify_retrieve_out outarg;
-       struct fuse_mount *fm;
-       struct inode *inode;
-       u64 nodeid;
-       int err;
-
-       if (size != sizeof(outarg))
-               return -EINVAL;
-
-       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
-       if (err)
-               return err;
-
-       fuse_copy_finish(cs);
-
-       if (outarg.offset >= MAX_LFS_FILESIZE)
-               return -EINVAL;
-
-       down_read(&fc->killsb);
-       err = -ENOENT;
-       nodeid = outarg.nodeid;
-
-       inode = fuse_ilookup(fc, nodeid, &fm);
-       if (inode) {
-               err = fuse_retrieve(fm, inode, &outarg);
-               iput(inode);
-       }
-       up_read(&fc->killsb);
-
-       return err;
-}
-
 /*
  * Resending all processing queue requests.
  *
@@ -2070,7 +1733,7 @@ static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size,
  * if the FUSE daemon takes careful measures to avoid processing duplicated
  * non-idempotent requests.
  */
-static void fuse_resend(struct fuse_chan *fch)
+void fuse_chan_resend(struct fuse_chan *fch)
 {
        struct fuse_dev *fud;
        struct fuse_req *req, *next;
@@ -2122,108 +1785,6 @@ static void fuse_resend(struct fuse_chan *fch)
        fuse_dev_wake_and_unlock(fiq);
 }
 
-static int fuse_notify_resend(struct fuse_conn *fc)
-{
-       fuse_resend(fc->chan);
-       return 0;
-}
-
-/*
- * Increments the fuse connection epoch.  This will result of dentries from
- * previous epochs to be invalidated.  Additionally, if inval_wq is set, a work
- * queue is scheduled to trigger the invalidation.
- */
-static int fuse_notify_inc_epoch(struct fuse_conn *fc)
-{
-       atomic_inc(&fc->epoch);
-       if (inval_wq)
-               schedule_work(&fc->epoch_work);
-
-       return 0;
-}
-
-static int fuse_notify_prune(struct fuse_conn *fc, unsigned int size,
-                            struct fuse_copy_state *cs)
-{
-       struct fuse_notify_prune_out outarg;
-       const unsigned int batch = 512;
-       u64 *nodeids __free(kfree) = kmalloc(sizeof(u64) * batch, GFP_KERNEL);
-       unsigned int num, i;
-       int err;
-
-       if (!nodeids)
-               return -ENOMEM;
-
-       if (size < sizeof(outarg))
-               return -EINVAL;
-
-       err = fuse_copy_one(cs, &outarg, sizeof(outarg));
-       if (err)
-               return err;
-
-       if (size - sizeof(outarg) != array_size(outarg.count, sizeof(u64)))
-               return -EINVAL;
-
-       for (; outarg.count; outarg.count -= num) {
-               num = min(batch, outarg.count);
-               err = fuse_copy_one(cs, nodeids, num * sizeof(u64));
-               if (err)
-                       return err;
-
-               scoped_guard(rwsem_read, &fc->killsb) {
-                       for (i = 0; i < num; i++)
-                               fuse_try_prune_one_inode(fc, nodeids[i]);
-               }
-       }
-       return 0;
-}
-
-static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
-                      unsigned int size, struct fuse_copy_state *cs)
-{
-       /*
-        * Only allow notifications during while the connection is in an
-        * initialized and connected state
-        */
-       if (!fc->chan->initialized || !fc->chan->connected)
-               return -EINVAL;
-
-       /* Don't try to move folios (yet) */
-       cs->move_folios = false;
-
-       switch (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);
-
-       case FUSE_NOTIFY_STORE:
-               return fuse_notify_store(fc, size, cs);
-
-       case FUSE_NOTIFY_RETRIEVE:
-               return fuse_notify_retrieve(fc, size, cs);
-
-       case FUSE_NOTIFY_DELETE:
-               return fuse_notify_delete(fc, size, cs);
-
-       case FUSE_NOTIFY_RESEND:
-               return fuse_notify_resend(fc);
-
-       case FUSE_NOTIFY_INC_EPOCH:
-               return fuse_notify_inc_epoch(fc);
-
-       case FUSE_NOTIFY_PRUNE:
-               return fuse_notify_prune(fc, size, cs);
-
-       default:
-               return -EINVAL;
-       }
-}
-
 /* Look up request on processing list by unique ID */
 struct fuse_req *fuse_request_find(struct fuse_pqueue *fpq, u64 unique)
 {
@@ -2298,6 +1859,17 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
         * and error contains notification code.
         */
        if (!oh.unique) {
+               /*
+                * Only allow notifications during while the connection is in an
+                * initialized and connected state
+                */
+               err = -EINVAL;
+               if (!fch->initialized || !fch->connected)
+                       goto copy_finish;
+
+               /* Don't try to move folios (yet) */
+               cs->move_folios = false;
+
                err = fuse_notify(fch->conn, oh.error, nbytes - sizeof(oh), cs);
                goto copy_finish;
        }
index 2c4d0c53ffbb4af3201c018f8311410b1273fa78..2bf4aba256d12e6c57c580c47c9bd0087f7e4b0d 100644 (file)
@@ -12,7 +12,10 @@ struct fuse_conn;
 struct fuse_chan;
 struct fuse_dev;
 struct fuse_args;
+struct fuse_copy_state;
 struct file;
+struct folio;
+enum fuse_notify_code;
 
 struct fuse_chan *fuse_chan_new(void);
 struct fuse_chan *fuse_dev_chan_new(void);
@@ -28,12 +31,12 @@ void fuse_chan_io_uring_enable(struct fuse_chan *fch);
 ssize_t fuse_chan_send(struct fuse_chan *fch, struct fuse_args *args);
 int fuse_chan_send_bg(struct fuse_chan *fch, struct fuse_args *args, gfp_t gfp_flags);
 int fuse_chan_send_notify_reply(struct fuse_chan *fch, struct fuse_args *args, u64 unique);
+void fuse_chan_resend(struct fuse_chan *fch);
 
 struct fuse_forget_link *fuse_alloc_forget(void);
 void fuse_chan_queue_forget(struct fuse_chan *fch, struct fuse_forget_link *forget,
                            u64 nodeid, u64 nlookup);
 
-
 DEFINE_FREE(fuse_chan_free, struct fuse_chan *, if (_T) fuse_chan_free(_T))
 
 void fuse_dev_install(struct fuse_dev *fud, struct fuse_chan *fch);
@@ -50,6 +53,13 @@ void fuse_chan_abort(struct fuse_chan *fch, bool abort_with_err);
 void fuse_chan_wait_aborted(struct fuse_chan *fch);
 
 void fuse_end_polls(struct fuse_conn *fc);
+int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
+               unsigned int size, struct fuse_copy_state *cs);
+
+int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size);
+int fuse_copy_folio(struct fuse_copy_state *cs, struct folio **foliop,
+                   unsigned offset, unsigned count, int zeroing);
+void fuse_copy_finish(struct fuse_copy_state *cs);
 
 #ifdef CONFIG_FUSE_IO_URING
 bool fuse_uring_enabled(void);
index 9b5ebc6b7762f56045380df8a83906f06e7ca126..35475a2f6f0a8cbbde2306b32f73f52cf36f12b3 100644 (file)
@@ -355,7 +355,6 @@ void fuse_request_bg_finish(struct fuse_chan *fch, struct fuse_req *req);
 
 void fuse_copy_init(struct fuse_copy_state *cs, bool write,
                           struct iov_iter *iter);
-void fuse_copy_finish(struct fuse_copy_state *cs);
 int fuse_copy_args(struct fuse_copy_state *cs, unsigned int numargs,
                   unsigned int argpages, struct fuse_arg *args,
                   int zeroing);