diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2017-07-04 16:03:17 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2017-07-04 16:03:17 -0400 |
commit | b9ac5c274b8c9d642567022c0e319bca4db31956 (patch) | |
tree | b28e97ec8acea6f9f752bb27c4bd8d3b55c36427 | |
parent | 415543d5c64fe490b4b6a7e21c3ea2f1310c442f (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.c | 46 | ||||
-rw-r--r-- | fs/overlayfs/namei.c | 4 | ||||
-rw-r--r-- | fs/overlayfs/util.c | 3 |
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 | ||
470 | static 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 | |||
470 | struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry) | 489 | struct 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); |
503 | out: | 535 | out: |
504 | return inode; | 536 | return inode; |
537 | |||
538 | out_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 | } |