[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250804173228.1990317-2-paullawrence@google.com>
Date: Mon, 4 Aug 2025 10:32:27 -0700
From: Paul Lawrence <paullawrence@...gle.com>
To: amir73il@...il.com
Cc: bernd.schubert@...tmail.fm, linux-fsdevel@...r.kernel.org,
linux-kernel@...r.kernel.org, miklos@...redi.hu, paullawrence@...gle.com
Subject: [PATCH 1/2] fuse: Allow backing file to be set at lookup (WIP)
Add optional extra outarg to FUSE_LOOKUP which holds a backing id to set
a backing file at lookup.
Signed-off-by: Paul Lawrence <paullawrence@...gle.com>
---
fs/fuse/dir.c | 23 ++++++++++++++++++----
fs/fuse/fuse_i.h | 3 +++
fs/fuse/iomode.c | 41 +++++++++++++++++++++++++++++++++++----
fs/fuse/passthrough.c | 40 +++++++++++++++++++++++++++++---------
include/uapi/linux/fuse.h | 4 ++++
5 files changed, 94 insertions(+), 17 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 62508d212826..c0bef93dd078 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -170,7 +170,8 @@ static void fuse_invalidate_entry(struct dentry *entry)
static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
u64 nodeid, const struct qstr *name,
- struct fuse_entry_out *outarg)
+ struct fuse_entry_out *outarg,
+ struct fuse_entry_passthrough_out *backing)
{
memset(outarg, 0, sizeof(struct fuse_entry_out));
args->opcode = FUSE_LOOKUP;
@@ -184,6 +185,12 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
args->out_numargs = 1;
args->out_args[0].size = sizeof(struct fuse_entry_out);
args->out_args[0].value = outarg;
+ if (backing) {
+ args->out_numargs = 2;
+ args->out_args[1].size = sizeof(struct fuse_entry_passthrough_out);
+ args->out_args[1].value = backing;
+ args->out_argvar = true;
+ }
}
/*
@@ -236,7 +243,7 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name,
attr_version = fuse_get_attr_version(fm->fc);
fuse_lookup_init(fm->fc, &args, get_node_id(dir),
- name, &outarg);
+ name, &outarg, NULL);
ret = fuse_simple_request(fm, &args);
/* Zero nodeid is same as -ENOENT */
if (!ret && !outarg.nodeid)
@@ -369,6 +376,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
struct fuse_forget_link *forget;
u64 attr_version, evict_ctr;
int err;
+ struct fuse_entry_passthrough_out passthrough;
*inode = NULL;
err = -ENAMETOOLONG;
@@ -384,10 +392,10 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
attr_version = fuse_get_attr_version(fm->fc);
evict_ctr = fuse_get_evict_ctr(fm->fc);
- fuse_lookup_init(fm->fc, &args, nodeid, name, outarg);
+ fuse_lookup_init(fm->fc, &args, nodeid, name, outarg, &passthrough);
err = fuse_simple_request(fm, &args);
/* Zero nodeid is same as -ENOENT, but with valid timeout */
- if (err || !outarg->nodeid)
+ if (err < 0 || !outarg->nodeid)
goto out_put_forget;
err = -EIO;
@@ -406,6 +414,13 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
fuse_queue_forget(fm->fc, forget, outarg->nodeid, 1);
goto out;
}
+
+ // TODO check that if fuse_backing is already set they are consistent
+ if (args.out_args[1].size && !get_fuse_inode(*inode)->fb) {
+ err = fuse_inode_set_passthrough(*inode, passthrough.backing_id);
+ if (err)
+ goto out;
+ }
err = 0;
out_put_forget:
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 1e8e732a2f09..aebd338751f1 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1595,9 +1595,12 @@ ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos,
ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
struct file *out, loff_t *ppos,
size_t len, unsigned int flags);
+struct fuse_backing *fuse_backing_id_get(struct fuse_conn *fc, int backing_id);
ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma);
int fuse_passthrough_readdir(struct file *file, struct dir_context *ctx);
+int fuse_inode_set_passthrough(struct inode *inode, int backing_id);
+
static inline struct fuse_backing *fuse_inode_passthrough(struct fuse_inode *fi)
{
#ifdef CONFIG_FUSE_PASSTHROUGH
diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c
index f46dfa040e53..4c23ae640624 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -166,6 +166,37 @@ static void fuse_file_uncached_io_release(struct fuse_file *ff,
fuse_inode_uncached_io_end(fi);
}
+/* Setup passthrough for inode operations without an open file */
+int fuse_inode_set_passthrough(struct inode *inode, int backing_id)
+{
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ struct fuse_backing *fb;
+ int err;
+
+ if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough_ino)
+ return 0;
+
+ /* backing inode is set once for the lifetime of the inode */
+ if (fuse_inode_passthrough(fi))
+ return 0;
+
+ fb = fuse_backing_id_get(fc, backing_id);
+ err = PTR_ERR(fb);
+ if (IS_ERR(fb))
+ goto fail;
+
+ fi->fb = fb;
+ set_bit(FUSE_I_PASSTHROUGH, &fi->state);
+ fi->iocachectr--;
+ return 0;
+
+fail:
+ pr_debug("failed to setup backing inode (ino=%lu, backing_id=%d, err=%i).\n",
+ inode->i_ino, backing_id, err);
+ return err;
+}
+
/*
* Open flags that are allowed in combination with FOPEN_PASSTHROUGH.
* A combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO means that read/write
@@ -185,8 +216,10 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
int err;
/* Check allowed conditions for file open in passthrough mode */
- if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough ||
- (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK))
+ if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) || !fc->passthrough)
+ return -EINVAL;
+
+ if (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK && !fuse_inode_backing(get_fuse_inode(inode)))
return -EINVAL;
fb = fuse_passthrough_open(file, inode,
@@ -224,8 +257,8 @@ int fuse_file_io_open(struct file *file, struct inode *inode)
* which is already open for passthrough.
*/
err = -EINVAL;
- if (fuse_inode_backing(fi) && !(ff->open_flags & FOPEN_PASSTHROUGH))
- goto fail;
+ if (fuse_inode_backing(fi))
+ ff->open_flags |= FOPEN_PASSTHROUGH;
/*
* FOPEN_PARALLEL_DIRECT_WRITES requires FOPEN_DIRECT_IO.
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index de6ece996ff8..cee40e1c6e4a 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -229,7 +229,6 @@ static int fuse_backing_id_free(int id, void *p, void *data)
{
struct fuse_backing *fb = p;
- WARN_ON_ONCE(refcount_read(&fb->count) != 1);
fuse_backing_free(fb);
return 0;
}
@@ -348,6 +347,29 @@ int fuse_backing_close(struct fuse_conn *fc, int backing_id)
return err;
}
+/*
+ * Get fuse backing object by backing id.
+ *
+ * Returns an fb object with elevated refcount to be stored in fuse inode.
+ */
+struct fuse_backing *fuse_backing_id_get(struct fuse_conn *fc, int backing_id)
+{
+ struct fuse_backing *fb;
+
+ if (backing_id <= 0)
+ return ERR_PTR(-EINVAL);
+
+ rcu_read_lock();
+ fb = idr_find(&fc->backing_files_map, backing_id);
+ fb = fuse_backing_get(fb);
+ rcu_read_unlock();
+
+ if (!fb)
+ return ERR_PTR(-ENOENT);
+
+ return fb;
+}
+
/*
* Setup passthrough to a backing file.
*
@@ -363,18 +385,18 @@ struct fuse_backing *fuse_passthrough_open(struct file *file,
struct file *backing_file;
int err;
- err = -EINVAL;
- if (backing_id <= 0)
- goto out;
-
rcu_read_lock();
- fb = idr_find(&fc->backing_files_map, backing_id);
+ if (backing_id <= 0) {
+ err = -EINVAL;
+ fb = fuse_inode_backing(get_fuse_inode(inode));
+ } else {
+ err = -ENOENT;
+ fb = idr_find(&fc->backing_files_map, backing_id);
+ }
fb = fuse_backing_get(fb);
- rcu_read_unlock();
-
- err = -ENOENT;
if (!fb)
goto out;
+ rcu_read_unlock();
/* Allocate backing file per fuse file to store fuse path */
backing_file = backing_file_open(&file->f_path, file->f_flags,
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index ff769766b748..6dbb045c794d 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -695,6 +695,10 @@ struct fuse_entry_out {
struct fuse_attr attr;
};
+struct fuse_entry_passthrough_out {
+ int32_t backing_id;
+};
+
struct fuse_forget_in {
uint64_t nlookup;
};
--
2.50.1.565.gc32cd1483b-goog
Powered by blists - more mailing lists