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>
This commit is contained in:
Miklos Szeredi 2026-09-21 10:47:32 +02:00
parent 2f19d54c05
commit c712cd3625
6 changed files with 45 additions and 31 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -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 {