From 4ad093cae17868dc367e0958ab5ceb8bb604f4e7 Mon Sep 17 00:00:00 2001 From: Dmitrii Merkurev Date: Wed, 29 Jun 2022 22:17:20 +0000 Subject: [PATCH] ANDROID: fuse-bpf: Make inodes with backing_fd reachable for regular FUSE fuse_iget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, when we’re trying to find inode based on their backing inode we strictly checking on nodeid == 0, so basically we’re not supporting nodeid != 0 for inode, which is backed by another one. Alongside with this, we’re using backing_inode as a hash for inode which make this inode not reachable for regular FUSE fuse_iget that as a result causing backing_inode losing because instead of getting existent one (with backing inode) we create a new one as a part of readdirplus. For more details please check: go/fuse-loosing-inode-with-backing Bug: 219958836 Test: Manually checked that /data and /obb inodes always have inode numbers configured. Co-developed-by: Paul Lawrence Change-Id: If6a5fb340561ac6320d3c4e86215f1bcd4c2c10c Signed-off-by: Dmitrii Merkurev --- fs/fuse/backing.c | 34 ++++++++++++++++++++++++---------- fs/fuse/dir.c | 2 +- fs/fuse/fuse_i.h | 3 ++- fs/fuse/inode.c | 31 ++++++++++++++++++++++++++++--- 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index b68f4f534974..0c39771a9527 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -262,6 +262,8 @@ int fuse_create_open_backing( struct dentry *newent; int err = 0; const struct fuse_create_in *fci = fa->in_args[0].value; + struct fuse_inode *fuse_inode = get_fuse_inode(entry->d_inode); + u64 target_nodeid = 0; if (!dir_fuse_inode || !dir_fuse_dentry) return -EIO; @@ -293,7 +295,10 @@ int fuse_create_open_backing( }; path_get(&get_fuse_dentry(entry)->backing_path); - inode = fuse_iget_backing(dir->i_sb, + if (fuse_inode) + target_nodeid = fuse_inode->nodeid; + + inode = fuse_iget_backing(dir->i_sb, target_nodeid, get_fuse_dentry(entry)->backing_path.dentry->d_inode); if (IS_ERR(inode)) { err = PTR_ERR(inode); @@ -1173,9 +1178,11 @@ struct dentry *fuse_lookup_finalize(struct fuse_bpf_args *fa, struct inode *dir, struct fuse_dentry *fd; struct dentry *bd; struct inode *inode, *backing_inode; + struct fuse_inode *fuse_inode = get_fuse_inode(entry->d_inode); struct fuse_entry_out *feo = fa->out_args[0].value; struct fuse_entry_bpf_out *febo = fa->out_args[1].value; struct fuse_entry_bpf *feb = container_of(febo, struct fuse_entry_bpf, out); + u64 target_nodeid = 0; fd = get_fuse_dentry(entry); if (!fd) @@ -1187,7 +1194,10 @@ struct dentry *fuse_lookup_finalize(struct fuse_bpf_args *fa, struct inode *dir, if (!backing_inode) return 0; - inode = fuse_iget_backing(dir->i_sb, backing_inode); + if (fuse_inode) + target_nodeid = fuse_inode->nodeid; + + inode = fuse_iget_backing(dir->i_sb, target_nodeid, backing_inode); if (IS_ERR(inode)) return ERR_PTR(PTR_ERR(inode)); @@ -1349,7 +1359,8 @@ int fuse_mknod_backing( { int err = 0; const struct fuse_mknod_in *fmi = fa->in_args[0].value; - struct inode *backing_inode = get_fuse_inode(dir)->backing_inode; + struct fuse_inode *fuse_inode = get_fuse_inode(dir); + struct inode *backing_inode = fuse_inode->backing_inode; struct path backing_path = {}; struct inode *inode = NULL; @@ -1376,7 +1387,7 @@ int fuse_mknod_backing( */ goto out; } - inode = fuse_iget_backing(dir->i_sb, backing_inode); + inode = fuse_iget_backing(dir->i_sb, fuse_inode->nodeid, backing_inode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out; @@ -1425,7 +1436,8 @@ int fuse_mkdir_backing( { int err = 0; const struct fuse_mkdir_in *fmi = fa->in_args[0].value; - struct inode *backing_inode = get_fuse_inode(dir)->backing_inode; + struct fuse_inode *fuse_inode = get_fuse_inode(dir); + struct inode *backing_inode = fuse_inode->backing_inode; struct path backing_path = {}; struct inode *inode = NULL; struct dentry *d; @@ -1453,7 +1465,7 @@ int fuse_mkdir_backing( dput(backing_path.dentry); backing_path.dentry = d; } - inode = fuse_iget_backing(dir->i_sb, backing_inode); + inode = fuse_iget_backing(dir->i_sb, fuse_inode->nodeid, backing_inode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out; @@ -1768,7 +1780,8 @@ int fuse_link_backing(struct fuse_bpf_args *fa, struct dentry *entry, struct path backing_new_path = {}; struct dentry *backing_dir_dentry; struct inode *fuse_new_inode = NULL; - struct inode *backing_dir_inode = get_fuse_inode(dir)->backing_inode; + struct fuse_inode *fuse_dir_inode = get_fuse_inode(dir); + struct inode *backing_dir_inode = fuse_dir_inode->backing_inode; get_fuse_backing_path(entry, &backing_old_path); if (!backing_old_path.dentry) @@ -1799,7 +1812,7 @@ int fuse_link_backing(struct fuse_bpf_args *fa, struct dentry *entry, goto out; } - fuse_new_inode = fuse_iget_backing(dir->i_sb, backing_dir_inode); + fuse_new_inode = fuse_iget_backing(dir->i_sb, fuse_dir_inode->nodeid, backing_dir_inode); if (IS_ERR(fuse_new_inode)) { err = PTR_ERR(fuse_new_inode); goto out; @@ -2163,7 +2176,8 @@ int fuse_symlink_backing( struct inode *dir, struct dentry *entry, const char *link, int len) { int err = 0; - struct inode *backing_inode = get_fuse_inode(dir)->backing_inode; + struct fuse_inode *fuse_inode = get_fuse_inode(dir); + struct inode *backing_inode = fuse_inode->backing_inode; struct path backing_path = {}; struct inode *inode = NULL; @@ -2186,7 +2200,7 @@ int fuse_symlink_backing( */ goto out; } - inode = fuse_iget_backing(dir->i_sb, backing_inode); + inode = fuse_iget_backing(dir->i_sb, fuse_inode->nodeid, backing_inode); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 541fad4362e3..b8debd29573c 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -545,7 +545,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name goto out_queue_forget; backing_inode = backing_file->f_inode; - *inode = fuse_iget_backing(sb, backing_inode); + *inode = fuse_iget_backing(sb, outarg->nodeid, backing_inode); if (!*inode) goto bpf_arg_out; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 00e0c53242e7..38bdf467ee64 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -976,9 +976,10 @@ extern const struct dentry_operations fuse_dentry_operations; extern const struct dentry_operations fuse_root_dentry_operations; /** - * Get a filled in inode + * Get a filled-in inode */ struct inode *fuse_iget_backing(struct super_block *sb, + u64 nodeid, struct inode *backing_inode); struct inode *fuse_iget(struct super_block *sb, u64 nodeid, int generation, struct fuse_attr *attr, diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index fa692f9e4ef3..e0df5e7fa857 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -330,6 +330,15 @@ static int fuse_inode_eq(struct inode *inode, void *_nodeidp) (struct fuse_inode_identifier *) _nodeidp; struct fuse_inode *fi = get_fuse_inode(inode); + return fii->nodeid == fi->nodeid; +} + +static int fuse_inode_backing_eq(struct inode *inode, void *_nodeidp) +{ + struct fuse_inode_identifier *fii = + (struct fuse_inode_identifier *) _nodeidp; + struct fuse_inode *fi = get_fuse_inode(inode); + return fii->nodeid == fi->nodeid #ifdef CONFIG_FUSE_BPF && fii->backing_inode == fi->backing_inode @@ -344,6 +353,17 @@ static int fuse_inode_set(struct inode *inode, void *_nodeidp) struct fuse_inode *fi = get_fuse_inode(inode); fi->nodeid = fii->nodeid; + + return 0; +} + +static int fuse_inode_backing_set(struct inode *inode, void *_nodeidp) +{ + struct fuse_inode_identifier *fii = + (struct fuse_inode_identifier *) _nodeidp; + struct fuse_inode *fi = get_fuse_inode(inode); + + fi->nodeid = fii->nodeid; #ifdef CONFIG_FUSE_BPF fi->backing_inode = fii->backing_inode; if (fi->backing_inode) @@ -353,20 +373,25 @@ static int fuse_inode_set(struct inode *inode, void *_nodeidp) return 0; } -struct inode *fuse_iget_backing(struct super_block *sb, +struct inode *fuse_iget_backing(struct super_block *sb, u64 nodeid, struct inode *backing_inode) { struct inode *inode; struct fuse_inode *fi; struct fuse_conn *fc = get_fuse_conn_super(sb); struct fuse_inode_identifier fii = { + .nodeid = nodeid, .backing_inode = backing_inode, }; struct fuse_attr attr; + unsigned long hash = (unsigned long) backing_inode; + + if (nodeid) + hash = nodeid; fuse_fill_attr_from_inode(&attr, backing_inode); - inode = iget5_locked(sb, (unsigned long) backing_inode, fuse_inode_eq, - fuse_inode_set, &fii); + inode = iget5_locked(sb, hash, fuse_inode_backing_eq, + fuse_inode_backing_set, &fii); if (!inode) return NULL;