]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
ovl: hash overlay non-dir inodes by copy up origin
authorMiklos Szeredi <mszeredi@redhat.com>
Tue, 4 Jul 2017 20:03:17 +0000 (22:03 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Tue, 4 Jul 2017 20:03:17 +0000 (22:03 +0200)
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/inode.c
fs/overlayfs/namei.c
fs/overlayfs/util.c

index 35bb956af8e8a2e1c0aa13d66ada81458774cf3b..d9fe07defca33e742c9100a46e240f3d860093ff 100644 (file)
@@ -467,6 +467,25 @@ static int ovl_inode_set(struct inode *inode, void *data)
        return 0;
 }
 
+static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
+                            struct dentry *upperdentry)
+{
+       struct inode *lowerinode = lowerdentry ? d_inode(lowerdentry) : NULL;
+
+       /* Lower (origin) inode must match, even if NULL */
+       if (ovl_inode_lower(inode) != lowerinode)
+               return false;
+
+       /*
+        * Allow non-NULL __upperdentry in inode even if upperdentry is NULL.
+        * This happens when finding a lower alias for a copied up hard link.
+        */
+       if (upperdentry && ovl_inode_upper(inode) != d_inode(upperdentry))
+               return false;
+
+       return true;
+}
+
 struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
 {
        struct dentry *lowerdentry = ovl_dentry_lower(dentry);
@@ -476,12 +495,25 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
        if (!realinode)
                realinode = d_inode(lowerdentry);
 
-       if (upperdentry && !d_is_dir(upperdentry)) {
-               inode = iget5_locked(dentry->d_sb, (unsigned long) realinode,
-                                    ovl_inode_test, ovl_inode_set, realinode);
+       if (!S_ISDIR(realinode->i_mode) &&
+           (upperdentry || (lowerdentry && ovl_indexdir(dentry->d_sb)))) {
+               struct inode *key = d_inode(lowerdentry ?: upperdentry);
+
+               inode = iget5_locked(dentry->d_sb, (unsigned long) key,
+                                    ovl_inode_test, ovl_inode_set, key);
                if (!inode)
-                       goto out;
+                       goto out_nomem;
                if (!(inode->i_state & I_NEW)) {
+                       /*
+                        * Verify that the underlying files stored in the inode
+                        * match those in the dentry.
+                        */
+                       if (!ovl_verify_inode(inode, lowerdentry, upperdentry)) {
+                               iput(inode);
+                               inode = ERR_PTR(-ESTALE);
+                               goto out;
+                       }
+
                        dput(upperdentry);
                        goto out;
                }
@@ -490,7 +522,7 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
        } else {
                inode = new_inode(dentry->d_sb);
                if (!inode)
-                       goto out;
+                       goto out_nomem;
        }
        ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev);
        ovl_inode_init(inode, upperdentry, lowerdentry);
@@ -502,4 +534,8 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
                unlock_new_inode(inode);
 out:
        return inode;
+
+out_nomem:
+       inode = ERR_PTR(-ENOMEM);
+       goto out;
 }
index 4df37e805eb73bcf7de3928381b71b25d14ff165..f7fb0c91941971135606fab77b2ea9446468c533 100644 (file)
@@ -678,9 +678,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                upperdentry = dget(index);
 
        if (upperdentry || ctr) {
-               err = -ENOMEM;
                inode = ovl_get_inode(dentry, upperdentry);
-               if (!inode)
+               err = PTR_ERR(inode);
+               if (IS_ERR(inode))
                        goto out_free_oe;
 
                OVL_I(inode)->redirect = upperredirect;
index 90b50b8e75abcfaea9d4e1234708c10be36f3640..22ed51f80e58937d955f326d1c278a89c080ce1f 100644 (file)
@@ -236,7 +236,6 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
 {
        struct inode *upperinode = d_inode(upperdentry);
 
-       WARN_ON(!inode_unhashed(inode));
        WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode));
        WARN_ON(OVL_I(inode)->__upperdentry);
 
@@ -245,7 +244,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
         */
        smp_wmb();
        OVL_I(inode)->__upperdentry = upperdentry;
-       if (!S_ISDIR(upperinode->i_mode)) {
+       if (!S_ISDIR(upperinode->i_mode) && inode_unhashed(inode)) {
                inode->i_private = upperinode;
                __insert_inode_hash(inode, (unsigned long) upperinode);
        }