lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ