ovl: fix corner case of non-constant st_dev;st_ino
authorAmir Goldstein <amir73il@gmail.com>
Tue, 14 Jan 2020 20:17:25 +0000 (22:17 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Fri, 24 Jan 2020 08:46:45 +0000 (09:46 +0100)
On non-samefs overlay without xino, non pure upper inodes should use a
pseudo_dev assigned to each unique lower fs, but if lower layer is on the
same fs and upper layer, it has no pseudo_dev assigned.

In this overlay layers setup:
 - two filesystems, A and B
 - upper layer is on A
 - lower layer 1 is also on A
 - lower layer 2 is on B

Non pure upper overlay inode, whose origin is in layer 1 will have the
st_dev;st_ino values of the real lower inode before copy up and the
st_dev;st_ino values of the real upper inode after copy up.

Fix this inconsitency by assigning a unique pseudo_dev also for upper fs,
that will be used as st_dev value along with the lower inode st_dev for
overlay inodes in the case above.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/inode.c
fs/overlayfs/super.c

index 60f7b8f..79e8994 100644 (file)
@@ -121,13 +121,12 @@ static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid)
                 */
                stat->dev = dentry->d_sb->s_dev;
                stat->ino = dentry->d_inode->i_ino;
-       } else if (fsid) {
+       } else {
                /*
                 * For non-samefs setup, if we cannot map all layers st_ino
                 * to a unified address space, we need to make sure that st_dev
-                * is unique per lower fs. Upper layer uses real st_dev and
-                * lower layers use the unique anonymous bdev assigned to the
-                * lower fs.
+                * is unique per underlying fs, so we use the unique anonymous
+                * bdev assigned to the underlying fs.
                 */
                stat->dev = OVL_FS(dentry->d_sb)->fs[fsid].pseudo_dev;
        }
@@ -143,7 +142,6 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
        struct path realpath;
        const struct cred *old_cred;
        bool is_dir = S_ISDIR(dentry->d_inode->i_mode);
-       bool samefs = ovl_same_fs(dentry->d_sb);
        int fsid = 0;
        int err;
        bool metacopy_blocks = false;
@@ -198,13 +196,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
                            (!ovl_verify_lower(dentry->d_sb) &&
                             (is_dir || lowerstat.nlink == 1))) {
                                fsid = ovl_layer_lower(dentry)->fsid;
-                               /*
-                                * Cannot use origin st_dev;st_ino because
-                                * origin inode content may differ from overlay
-                                * inode content.
-                                */
-                               if (samefs || fsid)
-                                       stat->ino = lowerstat.ino;
+                               stat->ino = lowerstat.ino;
                        }
 
                        /*
index 7cf5436..dcdcb4e 100644 (file)
@@ -229,8 +229,7 @@ static void ovl_free_fs(struct ovl_fs *ofs)
                mntput(ofs->layers[i].mnt);
        }
        kfree(ofs->layers);
-       /* fs[0].pseudo_dev is either null or real upper st_dev */
-       for (i = 1; i < ofs->numfs; i++)
+       for (i = 0; i < ofs->numfs; i++)
                free_anon_bdev(ofs->fs[i].pseudo_dev);
        kfree(ofs->fs);
 
@@ -1341,13 +1340,19 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
        ofs->numlayer = 1;
 
        /*
-        * All lower layers that share the same fs as upper layer, use the real
-        * upper st_dev.
+        * All lower layers that share the same fs as upper layer, use the same
+        * pseudo_dev as upper layer.  Allocate fs[0].pseudo_dev even for lower
+        * only overlay to simplify ovl_fs_free().
         * is_lower will be set if upper fs is shared with a lower layer.
         */
+       err = get_anon_bdev(&ofs->fs[0].pseudo_dev);
+       if (err) {
+               pr_err("failed to get anonymous bdev for upper fs\n");
+               goto out;
+       }
+
        if (ofs->upper_mnt) {
                ofs->fs[0].sb = ofs->upper_mnt->mnt_sb;
-               ofs->fs[0].pseudo_dev = ofs->upper_mnt->mnt_sb->s_dev;
                ofs->fs[0].is_lower = false;
        }