aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2017-07-04 16:03:17 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2017-07-04 16:03:17 -0400
commitb9ac5c274b8c9d642567022c0e319bca4db31956 (patch)
treeb28e97ec8acea6f9f752bb27c4bd8d3b55c36427
parent415543d5c64fe490b4b6a7e21c3ea2f1310c442f (diff)
ovl: hash overlay non-dir inodes by copy up origin
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/overlayfs/inode.c46
-rw-r--r--fs/overlayfs/namei.c4
-rw-r--r--fs/overlayfs/util.c3
3 files changed, 44 insertions, 9 deletions
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 35bb956af8e8..d9fe07defca3 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -467,6 +467,25 @@ static int ovl_inode_set(struct inode *inode, void *data)
467 return 0; 467 return 0;
468} 468}
469 469
470static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
471 struct dentry *upperdentry)
472{
473 struct inode *lowerinode = lowerdentry ? d_inode(lowerdentry) : NULL;
474
475 /* Lower (origin) inode must match, even if NULL */
476 if (ovl_inode_lower(inode) != lowerinode)
477 return false;
478
479 /*
480 * Allow non-NULL __upperdentry in inode even if upperdentry is NULL.
481 * This happens when finding a lower alias for a copied up hard link.
482 */
483 if (upperdentry && ovl_inode_upper(inode) != d_inode(upperdentry))
484 return false;
485
486 return true;
487}
488
470struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry) 489struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
471{ 490{
472 struct dentry *lowerdentry = ovl_dentry_lower(dentry); 491 struct dentry *lowerdentry = ovl_dentry_lower(dentry);
@@ -476,12 +495,25 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
476 if (!realinode) 495 if (!realinode)
477 realinode = d_inode(lowerdentry); 496 realinode = d_inode(lowerdentry);
478 497
479 if (upperdentry && !d_is_dir(upperdentry)) { 498 if (!S_ISDIR(realinode->i_mode) &&
480 inode = iget5_locked(dentry->d_sb, (unsigned long) realinode, 499 (upperdentry || (lowerdentry && ovl_indexdir(dentry->d_sb)))) {
481 ovl_inode_test, ovl_inode_set, realinode); 500 struct inode *key = d_inode(lowerdentry ?: upperdentry);
501
502 inode = iget5_locked(dentry->d_sb, (unsigned long) key,
503 ovl_inode_test, ovl_inode_set, key);
482 if (!inode) 504 if (!inode)
483 goto out; 505 goto out_nomem;
484 if (!(inode->i_state & I_NEW)) { 506 if (!(inode->i_state & I_NEW)) {
507 /*
508 * Verify that the underlying files stored in the inode
509 * match those in the dentry.
510 */
511 if (!ovl_verify_inode(inode, lowerdentry, upperdentry)) {
512 iput(inode);
513 inode = ERR_PTR(-ESTALE);
514 goto out;
515 }
516
485 dput(upperdentry); 517 dput(upperdentry);
486 goto out; 518 goto out;
487 } 519 }
@@ -490,7 +522,7 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
490 } else { 522 } else {
491 inode = new_inode(dentry->d_sb); 523 inode = new_inode(dentry->d_sb);
492 if (!inode) 524 if (!inode)
493 goto out; 525 goto out_nomem;
494 } 526 }
495 ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev); 527 ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev);
496 ovl_inode_init(inode, upperdentry, lowerdentry); 528 ovl_inode_init(inode, upperdentry, lowerdentry);
@@ -502,4 +534,8 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
502 unlock_new_inode(inode); 534 unlock_new_inode(inode);
503out: 535out:
504 return inode; 536 return inode;
537
538out_nomem:
539 inode = ERR_PTR(-ENOMEM);
540 goto out;
505} 541}
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index 4df37e805eb7..f7fb0c919419 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -678,9 +678,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
678 upperdentry = dget(index); 678 upperdentry = dget(index);
679 679
680 if (upperdentry || ctr) { 680 if (upperdentry || ctr) {
681 err = -ENOMEM;
682 inode = ovl_get_inode(dentry, upperdentry); 681 inode = ovl_get_inode(dentry, upperdentry);
683 if (!inode) 682 err = PTR_ERR(inode);
683 if (IS_ERR(inode))
684 goto out_free_oe; 684 goto out_free_oe;
685 685
686 OVL_I(inode)->redirect = upperredirect; 686 OVL_I(inode)->redirect = upperredirect;
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 90b50b8e75ab..22ed51f80e58 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -236,7 +236,6 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
236{ 236{
237 struct inode *upperinode = d_inode(upperdentry); 237 struct inode *upperinode = d_inode(upperdentry);
238 238
239 WARN_ON(!inode_unhashed(inode));
240 WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode)); 239 WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode));
241 WARN_ON(OVL_I(inode)->__upperdentry); 240 WARN_ON(OVL_I(inode)->__upperdentry);
242 241
@@ -245,7 +244,7 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry)
245 */ 244 */
246 smp_wmb(); 245 smp_wmb();
247 OVL_I(inode)->__upperdentry = upperdentry; 246 OVL_I(inode)->__upperdentry = upperdentry;
248 if (!S_ISDIR(upperinode->i_mode)) { 247 if (!S_ISDIR(upperinode->i_mode) && inode_unhashed(inode)) {
249 inode->i_private = upperinode; 248 inode->i_private = upperinode;
250 __insert_inode_hash(inode, (unsigned long) upperinode); 249 __insert_inode_hash(inode, (unsigned long) upperinode);
251 } 250 }