Add backing_id_64 field to fuse_open_out to allow opening files with
64-bit server-allocated backing IDs.

When the server sets FUSE_BACKING_ID_64 in open_out.open_flags, 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.

Signed-off-by: Miklos Szeredi <[email protected]>
---
 fs/fuse/dir.c             |  3 ++-
 fs/fuse/file.c            |  6 +++++-
 fs/fuse/fuse_i.h          |  2 +-
 fs/fuse/iomode.c          | 34 ++++++++++++++++++++++++++++------
 fs/fuse/passthrough.c     | 28 ++++++----------------------
 include/uapi/linux/fuse.h |  2 ++
 6 files changed, 44 insertions(+), 31 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index f48fafccce4b..874ec7cfffb1 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 92976906ab05..1036c06fe35a 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 003ef3c35d7a..59afebc3dd2b 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 1a10bc65fb31..811ea603b776 100644
--- a/fs/fuse/iomode.c
+++ b/fs/fuse/iomode.c
@@ -160,7 +160,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 */
@@ -170,18 +172,38 @@ 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");
+
+               backing_id = outarg->backing_id;
+               if (backing_id <= 0)
+                       return fuse_EIO("invalid 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 313c8d7ffc09..4894842ad6d0 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -150,41 +150,25 @@ ssize_t fuse_passthrough_mmap(struct file *file, struct 
vm_area_struct *vma)
 
 /*
  * 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 c88d7094af44..60fb2add5e12 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 {
-- 
2.54.0


Reply via email to