aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2016-12-16 05:02:55 -0500
committerMiklos Szeredi <mszeredi@redhat.com>2016-12-16 05:02:55 -0500
commit2aff4534b6c48c465c2ba3bca310646652318e16 (patch)
treec804f35f78e281b322375f0873558b4daca2b741
parentc412ce498396097cb96b3e24e062752312a962c9 (diff)
ovl: check lower existence when removing
Currently ovl_lookup() checks existence of lower file even if there's a non-directory on upper (which is always opaque). This is done so that remove can decide whether a whiteout is needed or not. It would be better to defer this check to unlink, since most of the time the gathered information about opaqueness will be unused. This adds a helper ovl_lower_positive() that checks if there's anything on the lower layer(s). The following patches also introduce changes to how the "opaque" attribute is updated on directories: this attribute is added when the directory is creted or moved over a whiteout or object covering something on the lower layer. However following changes will allow the attribute to remain on the directory after being moved, even if the new location doesn't cover anything. Because of this, we need to check lower layers even for opaque directories, so that whiteout is only created when necessary. This function will later be also used to decide about marking a directory opaque, so deal with negative dentries as well. When dealing with negative, it's enough to check for being a whiteout If the dentry is positive but not upper then it also obviously needs whiteout/opaque. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/overlayfs/dir.c4
-rw-r--r--fs/overlayfs/overlayfs.h1
-rw-r--r--fs/overlayfs/super.c54
3 files changed, 56 insertions, 3 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index b559221b2d34..4582d5efc01e 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -734,7 +734,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
734 type = ovl_path_type(dentry); 734 type = ovl_path_type(dentry);
735 735
736 old_cred = ovl_override_creds(dentry->d_sb); 736 old_cred = ovl_override_creds(dentry->d_sb);
737 if (OVL_TYPE_PURE_UPPER(type)) 737 if (!ovl_lower_positive(dentry))
738 err = ovl_remove_upper(dentry, is_dir); 738 err = ovl_remove_upper(dentry, is_dir);
739 else 739 else
740 err = ovl_remove_and_whiteout(dentry, is_dir); 740 err = ovl_remove_and_whiteout(dentry, is_dir);
@@ -841,7 +841,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
841 } 841 }
842 842
843 if (overwrite) { 843 if (overwrite) {
844 if (old_opaque) { 844 if (ovl_lower_positive(old)) {
845 if (!ovl_dentry_is_whiteout(new)) { 845 if (!ovl_dentry_is_whiteout(new)) {
846 /* Whiteout source */ 846 /* Whiteout source */
847 flags |= RENAME_WHITEOUT; 847 flags |= RENAME_WHITEOUT;
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 51e7d58276ce..2bd933aa622b 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -161,6 +161,7 @@ struct dentry *ovl_workdir(struct dentry *dentry);
161int ovl_want_write(struct dentry *dentry); 161int ovl_want_write(struct dentry *dentry);
162void ovl_drop_write(struct dentry *dentry); 162void ovl_drop_write(struct dentry *dentry);
163bool ovl_dentry_is_opaque(struct dentry *dentry); 163bool ovl_dentry_is_opaque(struct dentry *dentry);
164bool ovl_lower_positive(struct dentry *dentry);
164bool ovl_dentry_is_whiteout(struct dentry *dentry); 165bool ovl_dentry_is_whiteout(struct dentry *dentry);
165void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); 166void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
166bool ovl_is_whiteout(struct dentry *dentry); 167bool ovl_is_whiteout(struct dentry *dentry);
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index aa0427dabea8..b58710b36157 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -429,7 +429,6 @@ static inline struct dentry *ovl_lookup_real(struct dentry *dir,
429 struct dentry *dentry; 429 struct dentry *dentry;
430 430
431 dentry = lookup_one_len_unlocked(name->name, dir, name->len); 431 dentry = lookup_one_len_unlocked(name->name, dir, name->len);
432
433 if (IS_ERR(dentry)) { 432 if (IS_ERR(dentry)) {
434 if (PTR_ERR(dentry) == -ENOENT) 433 if (PTR_ERR(dentry) == -ENOENT)
435 dentry = NULL; 434 dentry = NULL;
@@ -613,6 +612,59 @@ out:
613 return ERR_PTR(err); 612 return ERR_PTR(err);
614} 613}
615 614
615bool ovl_lower_positive(struct dentry *dentry)
616{
617 struct ovl_entry *oe = dentry->d_fsdata;
618 struct ovl_entry *poe = dentry->d_parent->d_fsdata;
619 const struct qstr *name = &dentry->d_name;
620 unsigned int i;
621 bool positive = false;
622 bool done = false;
623
624 /*
625 * If dentry is negative, then lower is positive iff this is a
626 * whiteout.
627 */
628 if (!dentry->d_inode)
629 return oe->opaque;
630
631 /* Negative upper -> positive lower */
632 if (!oe->__upperdentry)
633 return true;
634
635 /* Positive upper -> have to look up lower to see whether it exists */
636 for (i = 0; !done && !positive && i < poe->numlower; i++) {
637 struct dentry *this;
638 struct dentry *lowerdir = poe->lowerstack[i].dentry;
639
640 this = lookup_one_len_unlocked(name->name, lowerdir,
641 name->len);
642 if (IS_ERR(this)) {
643 switch (PTR_ERR(this)) {
644 case -ENOENT:
645 case -ENAMETOOLONG:
646 break;
647
648 default:
649 /*
650 * Assume something is there, we just couldn't
651 * access it.
652 */
653 positive = true;
654 break;
655 }
656 } else {
657 if (this->d_inode) {
658 positive = !ovl_is_whiteout(this);
659 done = true;
660 }
661 dput(this);
662 }
663 }
664
665 return positive;
666}
667
616struct file *ovl_path_open(struct path *path, int flags) 668struct file *ovl_path_open(struct path *path, int flags)
617{ 669{
618 return dentry_open(path, flags | O_NOATIME, current_cred()); 670 return dentry_open(path, flags | O_NOATIME, current_cred());