summaryrefslogtreecommitdiff
path: root/fs/overlayfs/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/overlayfs/dir.c')
-rw-r--r--fs/overlayfs/dir.c37
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 */