diff options
-rw-r--r-- | fs/overlayfs/dir.c | 37 |
1 files changed, 33 insertions, 4 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index bfabc65fdc74..0c5e79966957 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -150,12 +150,39 @@ static int ovl_dir_getattr(const struct path *path, struct kstat *stat, type = ovl_path_real(dentry, &realpath); old_cred = ovl_override_creds(dentry->d_sb); err = vfs_getattr(&realpath, stat, request_mask, flags); - revert_creds(old_cred); if (err) - return err; + goto out; + + /* + * When all layers are on the same fs, use the copy-up-origin st_ino, + * which is persistent, unique and constant across copy up. + * + * Otherwise the pair {real st_ino; overlay st_dev} is not unique, so + * use the non persistent overlay st_ino. + */ + if (ovl_same_sb(dentry->d_sb)) { + if (OVL_TYPE_ORIGIN(type)) { + struct kstat lowerstat; + + ovl_path_lower(dentry, &realpath); + err = vfs_getattr(&realpath, &lowerstat, + STATX_INO, flags); + if (err) + goto out; + + WARN_ON_ONCE(stat->dev != lowerstat.dev); + stat->ino = lowerstat.ino; + } + } else { + stat->ino = dentry->d_inode->i_ino; + } + /* + * Always use the overlay st_dev for directories, so 'find -xdev' will + * scan the entire overlay mount and won't cross the overlay mount + * boundaries. + */ stat->dev = dentry->d_sb->s_dev; - stat->ino = dentry->d_inode->i_ino; /* * It's probably not worth it to count subdirs to get the @@ -164,8 +191,10 @@ static int ovl_dir_getattr(const struct path *path, struct kstat *stat, */ if (OVL_TYPE_MERGE(type)) stat->nlink = 1; +out: + revert_creds(old_cred); - return 0; + return err; } /* Common operations required to be done after creation of file on upper */ |