On Thu, Mar 15, 2018 at 8:40 PM, Amir Goldstein <amir73il@xxxxxxxxx> wrote: > On Thu, Mar 15, 2018 at 4:30 PM, Eddie Horng <eddiehorng.tw@xxxxxxxxx> wrote: [...] > The problem *is* with nfsd+overlayfs, because nfsd verifies > in compose_entry_fh() that (dchild->d_inode->i_ino == ino), but it is not. > In that case, encode_entryplus_baggage() falls back to encoding xdr_zero. > In overlayfs stat.st_ino is consistent with readdir d_ino since kernel version > v4.15 and only for all layers on the same fs. > > However, there is no guaranty that inode->i_ino is the same as stat.st_ino. > Overlayfs exposes only stat.st_ino to user (as well as readdir d_ino), but > never (to my knowledge) does it expose inode->i_ino. > > There was a nfsd fix for a somewhat similar problem that went into v4.16-rc1: > 76c479480b9a nfsd: encode stat->mtime for getattr instead of inode->i_mtime > > The solution to the problem is to either convert all references of > nfsd to i_inode > with references to st_ino, or make sure to set inode->i_ino correctly for > overlayfs inodes. > > From first glimps, the change in nfsd looks non trivial. > The change to overlayfs seems doable, but didn't look closely yet. > Will try to come up with test patch for you. > Eddie, Please try this patch. It worked for me. Thanks, Amir.
From 5dfe75eaf1aec080d110df343aa339ad7b816670 Mon Sep 17 00:00:00 2001 From: Amir Goldstein <amir73il@xxxxxxxxx> Date: Thu, 15 Mar 2018 23:39:01 +0200 Subject: [PATCH] ovl: set i_ino to the value of st_ino for NFS export When NFS export is enabled and d_ino is consistent with st_ino (samefs), set the same value to i_ino, because nfsd readdirplus compares d_ino values to i_ino values of child entries. When called from ovl_new_inode(), ino arg is 0, so i_ino will be updated to real upper inode i_ino on ovl_inode_init() or ovl_inode_update(). Reported-by: Eddie Horng <eddiehorng.tw@xxxxxxxxx> Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/overlayfs/inode.c | 21 +++++++++++++++++---- fs/overlayfs/util.c | 8 +++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 3b1bd469accd..4689716f23d8 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -459,9 +459,20 @@ static inline void ovl_lockdep_annotate_inode_mutex_key(struct inode *inode) #endif } -static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev) +static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev, + unsigned long ino) { - inode->i_ino = get_next_ino(); + /* + * When NFS export is enabled and d_ino is consistent with st_ino + * (samefs), set the same value to i_ino, because nfsd readdirplus + * compares d_ino values to i_ino values of child entries. When called + * from ovl_new_inode(), ino arg is 0, so i_ino will be updated to real + * upper inode i_ino on ovl_inode_init() or ovl_inode_update(). + */ + if (inode->i_sb->s_export_op && ovl_same_sb(inode->i_sb)) + inode->i_ino = ino; + else + inode->i_ino = get_next_ino(); inode->i_mode = mode; inode->i_flags |= S_NOCMTIME; #ifdef CONFIG_FS_POSIX_ACL @@ -597,7 +608,7 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev) inode = new_inode(sb); if (inode) - ovl_fill_inode(inode, mode, rdev); + ovl_fill_inode(inode, mode, rdev, 0); return inode; } @@ -710,6 +721,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, struct inode *inode; bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, index); bool is_dir; + unsigned long ino = 0; if (!realinode) realinode = d_inode(lowerdentry); @@ -748,13 +760,14 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, if (!is_dir) nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink); set_nlink(inode, nlink); + ino = key->i_ino; } else { /* Lower hardlink that will be broken on copy up */ inode = new_inode(sb); if (!inode) goto out_nomem; } - ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev); + ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev, ino); ovl_inode_init(inode, upperdentry, lowerdentry); if (upperdentry && ovl_is_impuredir(upperdentry)) diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 930784a26623..493f9b76fbf6 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -279,12 +279,16 @@ void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect) void ovl_inode_init(struct inode *inode, struct dentry *upperdentry, struct dentry *lowerdentry) { + struct inode *realinode = d_inode(upperdentry ?: lowerdentry); + if (upperdentry) OVL_I(inode)->__upperdentry = upperdentry; if (lowerdentry) OVL_I(inode)->lower = igrab(d_inode(lowerdentry)); - ovl_copyattr(d_inode(upperdentry ?: lowerdentry), inode); + ovl_copyattr(realinode, inode); + if (!inode->i_ino) + inode->i_ino = realinode->i_ino; } void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) @@ -299,6 +303,8 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) smp_wmb(); OVL_I(inode)->__upperdentry = upperdentry; if (inode_unhashed(inode)) { + if (!inode->i_ino) + inode->i_ino = upperinode->i_ino; inode->i_private = upperinode; __insert_inode_hash(inode, (unsigned long) upperinode); } -- 2.7.4