ovl: allocate anon bdev per unique lower fs
[linux] / fs / overlayfs / super.c
index 9ee37c7..7d97d30 100644 (file)
@@ -236,11 +236,12 @@ static void ovl_free_fs(struct ovl_fs *ofs)
        if (ofs->upperdir_locked)
                ovl_inuse_unlock(ofs->upper_mnt->mnt_root);
        mntput(ofs->upper_mnt);
-       for (i = 0; i < ofs->numlower; i++) {
+       for (i = 0; i < ofs->numlower; i++)
                mntput(ofs->lower_layers[i].mnt);
-               free_anon_bdev(ofs->lower_layers[i].pseudo_dev);
-       }
+       for (i = 0; i < ofs->numlowerfs; i++)
+               free_anon_bdev(ofs->lower_fs[i].pseudo_dev);
        kfree(ofs->lower_layers);
+       kfree(ofs->lower_fs);
 
        kfree(ofs->config.lowerdir);
        kfree(ofs->config.upperdir);
@@ -1108,6 +1109,35 @@ out:
        return err;
 }
 
+/* Get a unique fsid for the layer */
+static int ovl_get_fsid(struct ovl_fs *ofs, struct super_block *sb)
+{
+       unsigned int i;
+       dev_t dev;
+       int err;
+
+       /* fsid 0 is reserved for upper fs even with non upper overlay */
+       if (ofs->upper_mnt && ofs->upper_mnt->mnt_sb == sb)
+               return 0;
+
+       for (i = 0; i < ofs->numlowerfs; i++) {
+               if (ofs->lower_fs[i].sb == sb)
+                       return i + 1;
+       }
+
+       err = get_anon_bdev(&dev);
+       if (err) {
+               pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n");
+               return err;
+       }
+
+       ofs->lower_fs[ofs->numlowerfs].sb = sb;
+       ofs->lower_fs[ofs->numlowerfs].pseudo_dev = dev;
+       ofs->numlowerfs++;
+
+       return ofs->numlowerfs;
+}
+
 static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
                                unsigned int numlower)
 {
@@ -1119,23 +1149,27 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
                                    GFP_KERNEL);
        if (ofs->lower_layers == NULL)
                goto out;
+
+       ofs->lower_fs = kcalloc(numlower, sizeof(struct ovl_sb),
+                               GFP_KERNEL);
+       if (ofs->lower_fs == NULL)
+               goto out;
+
        for (i = 0; i < numlower; i++) {
                struct vfsmount *mnt;
-               dev_t dev;
+               int fsid;
 
-               err = get_anon_bdev(&dev);
-               if (err) {
-                       pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n");
+               err = fsid = ovl_get_fsid(ofs, stack[i].mnt->mnt_sb);
+               if (err < 0)
                        goto out;
-               }
 
                mnt = clone_private_mount(&stack[i]);
                err = PTR_ERR(mnt);
                if (IS_ERR(mnt)) {
                        pr_err("overlayfs: failed to clone lowerpath\n");
-                       free_anon_bdev(dev);
                        goto out;
                }
+
                /*
                 * Make lower layers R/O.  That way fchmod/fchown on lower file
                 * will fail instead of modifying lower fs.
@@ -1143,15 +1177,13 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
                mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
 
                ofs->lower_layers[ofs->numlower].mnt = mnt;
-               ofs->lower_layers[ofs->numlower].pseudo_dev = dev;
                ofs->lower_layers[ofs->numlower].idx = i + 1;
+               ofs->lower_layers[ofs->numlower].fsid = fsid;
+               if (fsid) {
+                       ofs->lower_layers[ofs->numlower].fs =
+                               &ofs->lower_fs[fsid - 1];
+               }
                ofs->numlower++;
-
-               /* Check if all lower layers are on same sb */
-               if (i == 0)
-                       ofs->same_sb = mnt->mnt_sb;
-               else if (ofs->same_sb != mnt->mnt_sb)
-                       ofs->same_sb = NULL;
        }
        err = 0;
 out:
@@ -1305,8 +1337,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        /* If the upper fs is nonexistent, we mark overlayfs r/o too */
        if (!ofs->upper_mnt)
                sb->s_flags |= SB_RDONLY;
-       else if (ofs->upper_mnt->mnt_sb != ofs->same_sb)
-               ofs->same_sb = NULL;
 
        if (!(ovl_force_readonly(ofs)) && ofs->config.index) {
                err = ovl_get_indexdir(ofs, oe, &upperpath);
@@ -1359,6 +1389,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 
        /* Root is always merge -> can have whiteouts */
        ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry));
+       ovl_dentry_set_flag(OVL_E_CONNECTED, root_dentry);
        ovl_inode_init(d_inode(root_dentry), upperpath.dentry,
                       ovl_dentry_lower(root_dentry));