summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2026-09-21 10:47:32 +0200
committerMiklos Szeredi <mszeredi@redhat.com>2026-10-19 14:56:24 +0200
commitc712cd3625960e0bf233700f486bd34c27df2be4 (patch)
tree500702439015fe2514711057cfaaf9eac9debe7a
parent2f19d54c055d7db0fd78db638fd746efca4d5115 (diff)
downloadlinux-next-c712cd3625960e0bf233700f486bd34c27df2be4.tar.gz
linux-next-c712cd3625960e0bf233700f486bd34c27df2be4.zip
fuse: support opening 64 bit backing ID
Add backing_id_64 field to fuse_open_out to allow opening files with 64-bit server-allocated backing IDs. If the connection was initialized with FUSE_PASSTHROUGH_V2, the kernel reads the backing ID from open_out.backing_id_64 instead of open_out.backing_id. The open reply is now variable-length for backward compatibility with servers that don't send the new backing_id_64 field. Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/fuse/dir.c3
-rw-r--r--fs/fuse/file.c6
-rw-r--r--fs/fuse/fuse_i.h2
-rw-r--r--fs/fuse/iomode.c35
-rw-r--r--fs/fuse/passthrough.c28
-rw-r--r--include/uapi/linux/fuse.h2
6 files changed, 45 insertions, 31 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 4ee40f22500e..cb3a42ac3238 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -876,6 +876,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
args.out_args[0].value = &outentry;
/* Store outarg for fuse_finish_open() */
outopenp = &ff->args->open_outarg;
+ args.out_argvar = true; /* compat */
args.out_args[1].size = sizeof(*outopenp);
args.out_args[1].value = outopenp;
@@ -885,7 +886,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir,
err = fuse_simple_idmap_request(idmap, fm, &args);
free_ext_value(&args);
- if (err)
+ if (err < 0)
goto out_free_ff;
err = -EIO;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index eb04f08ac907..d6eb2bb182d1 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -28,6 +28,7 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
{
struct fuse_open_in inarg;
FUSE_ARGS(args);
+ int res;
memset(&inarg, 0, sizeof(inarg));
inarg.flags = open_flags & ~(O_CREAT | O_EXCL | O_NOCTTY);
@@ -45,10 +46,13 @@ static int fuse_send_open(struct fuse_mount *fm, u64 nodeid,
args.in_args[0].size = sizeof(inarg);
args.in_args[0].value = &inarg;
args.out_numargs = 1;
+ args.out_argvar = true; /* compat */
args.out_args[0].size = sizeof(*outargp);
args.out_args[0].value = outargp;
- return fuse_simple_request(fm, &args);
+ res = fuse_simple_request(fm, &args);
+
+ return res < 0 ? res : 0;
}
struct fuse_file *fuse_file_alloc(struct fuse_mount *fm, bool release)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index c1cc79bf6ea2..9e3a6206b7a7 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1314,7 +1314,7 @@ static inline struct fuse_backing *fuse_inode_backing_set(struct fuse_inode *fi,
#endif
}
-struct fuse_backing *fuse_passthrough_open(struct file *file, int backing_id);
+int fuse_passthrough_open(struct file *file, struct fuse_backing *fb);
void fuse_passthrough_release(struct fuse_file *ff, struct fuse_backing *fb);
static inline bool fuse_is_passthrough(struct fuse_file *ff)
diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c
index 1b8b267d0c0d..8b4774c80b11 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -165,7 +165,9 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
{
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_open_out *outarg = &ff->args->open_outarg;
struct fuse_backing *fb;
+ u64 backing_id;
int err;
/* Check allowed conditions for file open in passthrough mode */
@@ -175,18 +177,39 @@ static int fuse_file_passthrough_open(struct inode *inode, struct file *file)
if (ff->open_flags & ~FOPEN_PASSTHROUGH_MASK)
return fuse_EIO("conflicting open flags");
- fb = fuse_passthrough_open(file, ff->args->open_outarg.backing_id);
- if (IS_ERR(fb))
- return PTR_ERR(fb);
+ if (!fc->backing_id_64) {
+ if (outarg->backing_id_64 != 0)
+ return fuse_EIO("64 bit backing ID set");
+
+ if (outarg->backing_id <= 0)
+ return fuse_EIO("invalid backing ID");
+
+ backing_id = outarg->backing_id;
+ } else {
+ if (outarg->backing_id != 0)
+ return fuse_EIO("32 bit backing ID set");
+
+ backing_id = outarg->backing_id_64;
+ }
+ fb = fuse_backing_lookup(fc, backing_id);
+ if (!fb)
+ return fuse_EIO("backing not found");
+
+ err = fuse_passthrough_open(file, fb);
+ if (err)
+ goto backing_put;
/* First passthrough file open denies caching inode io mode */
err = fuse_file_uncached_io_open(inode, ff, fb);
- if (!err)
- return 0;
+ if (err)
+ goto passthrough_release;
+ return 0;
+
+passthrough_release:
fuse_passthrough_release(ff, fb);
+backing_put:
fuse_backing_put(fb);
-
return err;
}
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index 8f53ef8945d2..9bf8ce33f549 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -163,41 +163,25 @@ int fuse_passthrough_fadvise(struct fuse_file *ff, loff_t offset, loff_t len, in
/*
* Setup passthrough to a backing file.
- *
- * Returns an fb object with elevated refcount to be stored in fuse inode.
*/
-struct fuse_backing *fuse_passthrough_open(struct file *file, int backing_id)
+int fuse_passthrough_open(struct file *file, struct fuse_backing *fb)
{
struct fuse_file *ff = file->private_data;
- struct fuse_conn *fc = ff->fm->fc;
- struct fuse_backing *fb = NULL;
struct file *backing_file;
- if (fc->backing_id_64)
- return ERR_PTR(fuse_EIO("incompatible backing version"));
-
- if (backing_id <= 0)
- return ERR_PTR(fuse_EIO("invalid backing_id"));
-
- fb = fuse_backing_lookup(fc, backing_id);
- if (!fb)
- return ERR_PTR(fuse_EIO("backing not found"));
-
/* Allocate backing file per fuse file to store fuse path */
backing_file = backing_file_open(file, file->f_flags,
&fb->file->f_path, fb->cred);
- if (IS_ERR(backing_file)) {
- fuse_backing_put(fb);
- return ERR_PTR(fuse_EIO("failed to open backing file (%ld)", PTR_ERR(backing_file)));
- }
+ if (IS_ERR(backing_file))
+ return fuse_EIO("failed to open backing file (%ld)", PTR_ERR(backing_file));
ff->passthrough = backing_file;
ff->cred = get_cred(fb->cred);
- pr_debug("%s: backing_id=%d, fb=0x%p, backing_file=0x%p\n", __func__,
- backing_id, fb, ff->passthrough);
+ pr_debug("%s: backing_id=%llu, fb=0x%p, backing_file=0x%p\n", __func__,
+ fb->backing_id, fb, ff->passthrough);
- return fb;
+ return 0;
}
void fuse_passthrough_release(struct fuse_file *ff, struct fuse_backing *fb)
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 37c83c54d960..90e0a9be7df0 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -254,6 +254,7 @@
* - add FUSE_PASSTHROUGH_V2
* - add FUSE_DEV_IOC_BACKING_CREATE, struct fuse_backing_create_in
* - add FUSE_NOTIFY_BACKING_REMOVE, struct fuse_notify_backing_remove_out
+ * - add backing_id_64 to fuse_open_out
*/
#ifndef _LINUX_FUSE_H
@@ -841,6 +842,7 @@ struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
int32_t backing_id;
+ uint64_t backing_id_64;
};
struct fuse_release_in {