Use the new extended fuse_entry2_out reply for entry creation operations (mknod, mkdir, symlink, link) when FUSE_PASSTHROUGH_INO is enabled. If the server returns a backing id, the newly created inode will be automatically associated with the backing file for passthrough operations. If no backing id is returned (no passthrough), the statx attributes are cached. Signed-off-by: Joanne Koong --- fs/fuse/dir.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 3a6adae530da..a11f9e4c1999 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1088,9 +1088,12 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun struct dentry *entry, umode_t mode) { struct fuse_entry_out outarg; + struct fuse_entry2_out outarg2; struct inode *inode; struct dentry *d; struct fuse_forget_link *forget; + struct fuse_statx *sx = NULL; + int backing_id; int epoch, err; if (fuse_is_bad(dir)) @@ -1102,11 +1105,18 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun if (!forget) return ERR_PTR(-ENOMEM); - memset(&outarg, 0, sizeof(outarg)); args->nodeid = get_node_id(dir); args->out_numargs = 1; - args->out_args[0].size = sizeof(outarg); - args->out_args[0].value = &outarg; + + if (fuse_use_entry2(fm->fc)) { + memset(&outarg2, 0, sizeof(outarg2)); + args->out_args[0].size = sizeof(outarg2); + args->out_args[0].value = &outarg2; + } else { + memset(&outarg, 0, sizeof(outarg)); + args->out_args[0].size = sizeof(outarg); + args->out_args[0].value = &outarg; + } if (args->opcode != FUSE_LINK) { err = get_create_ext(idmap, args, dir, entry, mode); @@ -1119,21 +1129,35 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun if (err) goto out_put_forget_req; + backing_id = fuse_process_entry2(fm->fc, &outarg2, &outarg, &sx); + err = -EIO; if (invalid_nodeid(outarg.nodeid) || fuse_invalid_attr(&outarg.attr)) goto out_put_forget_req; + if (backing_id < 0) { + fuse_chan_queue_forget(fm->fc->chan, forget, outarg.nodeid, 1); + return ERR_PTR(backing_id); + } if ((outarg.attr.mode ^ mode) & S_IFMT) goto out_put_forget_req; inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, - &outarg.attr, NULL, ATTR_TIMEOUT(&outarg), 0, 0); + &outarg.attr, sx, ATTR_TIMEOUT(&outarg), 0, 0); if (!inode) { fuse_chan_queue_forget(fm->fc->chan, forget, outarg.nodeid, 1); return ERR_PTR(-ENOMEM); } kfree(forget); + if (backing_id) { + err = fuse_inode_set_passthrough(inode, backing_id); + if (err) { + iput(inode); + return ERR_PTR(err); + } + } + d_drop(entry); d = d_splice_alias(inode, entry); if (IS_ERR(d)) -- 2.52.0