A following patch adds fake overlay inodes, which are hashed by the same real inode as an overlay inode or a trap inode. Pass a key with the real inode and the kind of inode to ovl_inode_test() and ovl_inode_set(), so that all lookups share one test function. No functional change intended. Signed-off-by: Josef Bacik --- fs/overlayfs/inode.c | 57 ++++++++++++++++++++++++++++++++++++++---------- fs/overlayfs/overlayfs.h | 5 +++++ 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 5895ee5aa771..88cec47d0bc8 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -1069,17 +1069,50 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev) return inode; } +/* + * An overlay inode is hashed by a real inode. A real inode can have a fake + * overlay inode next to its overlay inode or its trap inode. + */ +struct ovl_inode_key { + struct inode *real; + bool fake; +}; + static int ovl_inode_test(struct inode *inode, void *data) { - return inode->i_private == data; + struct ovl_inode_key *key = data; + + return inode->i_private == key->real && + ovl_test_flag(OVL_FAKE, inode) == key->fake; } static int ovl_inode_set(struct inode *inode, void *data) { - inode->i_private = data; + struct ovl_inode_key *key = data; + + inode->i_private = key->real; + if (key->fake) + ovl_set_flag(OVL_FAKE, inode); return 0; } +static struct inode *ovl_ilookup(struct super_block *sb, struct inode *real, + bool fake) +{ + struct ovl_inode_key key = { .real = real, .fake = fake }; + + return ilookup5(sb, (unsigned long)real, ovl_inode_test, &key); +} + +static struct inode *ovl_iget_locked(struct super_block *sb, struct inode *real, + bool fake) +{ + struct ovl_inode_key key = { .real = real, .fake = fake }; + + return iget5_locked(sb, (unsigned long)real, ovl_inode_test, + ovl_inode_set, &key); +} + static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry, struct dentry *upperdentry, bool strict) { @@ -1123,7 +1156,7 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, { struct inode *inode, *key = d_inode(real); - inode = ilookup5(sb, (unsigned long) key, ovl_inode_test, key); + inode = ovl_ilookup(sb, key, false); if (!inode) return NULL; @@ -1142,7 +1175,7 @@ bool ovl_lookup_trap_inode(struct super_block *sb, struct dentry *dir) struct inode *trap; bool res; - trap = ilookup5(sb, (unsigned long) key, ovl_inode_test, key); + trap = ovl_ilookup(sb, key, false); if (!trap) return false; @@ -1166,8 +1199,7 @@ struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir) if (!d_is_dir(dir)) return ERR_PTR(-ENOTDIR); - trap = iget5_locked(sb, (unsigned long) key, ovl_inode_test, - ovl_inode_set, key); + trap = ovl_iget_locked(sb, key, false); if (!trap) return ERR_PTR(-ENOMEM); @@ -1218,12 +1250,15 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper, } static struct inode *ovl_iget5(struct super_block *sb, struct inode *newinode, - struct inode *key) + struct inode *real) { - return newinode ? inode_insert5(newinode, (unsigned long) key, - ovl_inode_test, ovl_inode_set, key) : - iget5_locked(sb, (unsigned long) key, - ovl_inode_test, ovl_inode_set, key); + struct ovl_inode_key key = { .real = real }; + + if (!newinode) + return ovl_iget_locked(sb, real, false); + + return inode_insert5(newinode, (unsigned long)real, ovl_inode_test, + ovl_inode_set, &key); } struct inode *ovl_get_inode(struct super_block *sb, diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index d3a897e46ccb..f884e6695d2c 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -66,6 +66,11 @@ enum ovl_inode_flag { OVL_CONST_INO, OVL_HAS_DIGEST, OVL_VERIFIED_DIGEST, + /* + * A fake inode: not the overlay inode or the trap inode of the real + * inode that it is hashed by. + */ + OVL_FAKE, }; enum ovl_entry_flag { -- 2.55.0