diff options
author | Amir Goldstein <amir73il@gmail.com> | 2017-12-24 11:28:04 -0500 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2018-01-24 05:26:03 -0500 |
commit | 9436a1a339fae84698aaa0b66d7a822018388348 (patch) | |
tree | 38d74ccb48e21ade05347aa0e4f2de2ad6bd17c0 | |
parent | f71bd9cfb692ec80236b186419bf907eb5fa348c (diff) |
ovl: decode lower file handles of unlinked but open files
Lookup overlay inode in cache by origin inode, so we can decode a file
handle of an open file even if the index has a whiteout index entry to
mark this overlay inode was unlinked.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r-- | fs/overlayfs/export.c | 23 | ||||
-rw-r--r-- | fs/overlayfs/inode.c | 16 | ||||
-rw-r--r-- | fs/overlayfs/overlayfs.h | 1 |
3 files changed, 38 insertions, 2 deletions
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index f475a10eec07..0bca38c79244 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c | |||
@@ -443,14 +443,22 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, | |||
443 | struct ovl_path *stack = &origin; | 443 | struct ovl_path *stack = &origin; |
444 | struct dentry *dentry = NULL; | 444 | struct dentry *dentry = NULL; |
445 | struct dentry *index = NULL; | 445 | struct dentry *index = NULL; |
446 | struct inode *inode = NULL; | ||
447 | bool is_deleted = false; | ||
446 | int err; | 448 | int err; |
447 | 449 | ||
448 | /* First lookup indexed upper by fh */ | 450 | /* First lookup indexed upper by fh */ |
449 | if (ofs->indexdir) { | 451 | if (ofs->indexdir) { |
450 | index = ovl_get_index_fh(ofs, fh); | 452 | index = ovl_get_index_fh(ofs, fh); |
451 | err = PTR_ERR(index); | 453 | err = PTR_ERR(index); |
452 | if (IS_ERR(index)) | 454 | if (IS_ERR(index)) { |
453 | return ERR_PTR(err); | 455 | if (err != -ESTALE) |
456 | return ERR_PTR(err); | ||
457 | |||
458 | /* Found a whiteout index - treat as deleted inode */ | ||
459 | is_deleted = true; | ||
460 | index = NULL; | ||
461 | } | ||
454 | } | 462 | } |
455 | 463 | ||
456 | /* Then lookup origin by fh */ | 464 | /* Then lookup origin by fh */ |
@@ -461,6 +469,16 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, | |||
461 | err = ovl_verify_origin(index, origin.dentry, false); | 469 | err = ovl_verify_origin(index, origin.dentry, false); |
462 | if (err) | 470 | if (err) |
463 | goto out_err; | 471 | goto out_err; |
472 | } else if (is_deleted) { | ||
473 | /* Lookup deleted non-dir by origin inode */ | ||
474 | if (!d_is_dir(origin.dentry)) | ||
475 | inode = ovl_lookup_inode(sb, origin.dentry); | ||
476 | err = -ESTALE; | ||
477 | if (!inode || atomic_read(&inode->i_count) == 1) | ||
478 | goto out_err; | ||
479 | |||
480 | /* Deleted but still open? */ | ||
481 | index = dget(ovl_i_dentry_upper(inode)); | ||
464 | } | 482 | } |
465 | 483 | ||
466 | dentry = ovl_get_dentry(sb, NULL, &origin, index); | 484 | dentry = ovl_get_dentry(sb, NULL, &origin, index); |
@@ -468,6 +486,7 @@ static struct dentry *ovl_lower_fh_to_d(struct super_block *sb, | |||
468 | out: | 486 | out: |
469 | dput(origin.dentry); | 487 | dput(origin.dentry); |
470 | dput(index); | 488 | dput(index); |
489 | iput(inode); | ||
471 | return dentry; | 490 | return dentry; |
472 | 491 | ||
473 | out_err: | 492 | out_err: |
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index bfd7c766b5cd..56ba015b9f5e 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c | |||
@@ -645,6 +645,22 @@ static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry, | |||
645 | return true; | 645 | return true; |
646 | } | 646 | } |
647 | 647 | ||
648 | struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *origin) | ||
649 | { | ||
650 | struct inode *inode, *key = d_inode(origin); | ||
651 | |||
652 | inode = ilookup5(sb, (unsigned long) key, ovl_inode_test, key); | ||
653 | if (!inode) | ||
654 | return NULL; | ||
655 | |||
656 | if (!ovl_verify_inode(inode, origin, NULL)) { | ||
657 | iput(inode); | ||
658 | return ERR_PTR(-ESTALE); | ||
659 | } | ||
660 | |||
661 | return inode; | ||
662 | } | ||
663 | |||
648 | struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, | 664 | struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, |
649 | struct dentry *lowerdentry, struct dentry *index, | 665 | struct dentry *lowerdentry, struct dentry *index, |
650 | unsigned int numlower) | 666 | unsigned int numlower) |
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 40ba11e412b1..a47f9142b6be 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h | |||
@@ -322,6 +322,7 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags); | |||
322 | bool ovl_is_private_xattr(const char *name); | 322 | bool ovl_is_private_xattr(const char *name); |
323 | 323 | ||
324 | struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev); | 324 | struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev); |
325 | struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *origin); | ||
325 | struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, | 326 | struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, |
326 | struct dentry *lowerdentry, struct dentry *index, | 327 | struct dentry *lowerdentry, struct dentry *index, |
327 | unsigned int numlower); | 328 | unsigned int numlower); |