diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b559221b2d34dc3532f0dc080c9603b4361fc492..4582d5efc01e187ac78f09b34fffa36450844395 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) type = ovl_path_type(dentry); old_cred = ovl_override_creds(dentry->d_sb); - if (OVL_TYPE_PURE_UPPER(type)) + if (!ovl_lower_positive(dentry)) err = ovl_remove_upper(dentry, is_dir); else err = ovl_remove_and_whiteout(dentry, is_dir); @@ -841,7 +841,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, } if (overwrite) { - if (old_opaque) { + if (ovl_lower_positive(old)) { if (!ovl_dentry_is_whiteout(new)) { /* Whiteout source */ flags |= RENAME_WHITEOUT; diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 51e7d58276ce84e713484fc678c36420ebe2b12b..2bd933aa622bf5fbb53796151eb5ec36b2fce9f9 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -161,6 +161,7 @@ struct dentry *ovl_workdir(struct dentry *dentry); int ovl_want_write(struct dentry *dentry); void ovl_drop_write(struct dentry *dentry); bool ovl_dentry_is_opaque(struct dentry *dentry); +bool ovl_lower_positive(struct dentry *dentry); bool ovl_dentry_is_whiteout(struct dentry *dentry); void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); bool ovl_is_whiteout(struct dentry *dentry); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index aa0427dabea85bf6ceba926733c7e7523d4bfd99..b58710b36157ee69f6c30e35d6dfef289ce6eedc 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, struct dentry *dentry; dentry = lookup_one_len_unlocked(name->name, dir, name->len); - if (IS_ERR(dentry)) { if (PTR_ERR(dentry) == -ENOENT) dentry = NULL; @@ -613,6 +612,59 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, return ERR_PTR(err); } +bool ovl_lower_positive(struct dentry *dentry) +{ + struct ovl_entry *oe = dentry->d_fsdata; + struct ovl_entry *poe = dentry->d_parent->d_fsdata; + const struct qstr *name = &dentry->d_name; + unsigned int i; + bool positive = false; + bool done = false; + + /* + * If dentry is negative, then lower is positive iff this is a + * whiteout. + */ + if (!dentry->d_inode) + return oe->opaque; + + /* Negative upper -> positive lower */ + if (!oe->__upperdentry) + return true; + + /* Positive upper -> have to look up lower to see whether it exists */ + for (i = 0; !done && !positive && i < poe->numlower; i++) { + struct dentry *this; + struct dentry *lowerdir = poe->lowerstack[i].dentry; + + this = lookup_one_len_unlocked(name->name, lowerdir, + name->len); + if (IS_ERR(this)) { + switch (PTR_ERR(this)) { + case -ENOENT: + case -ENAMETOOLONG: + break; + + default: + /* + * Assume something is there, we just couldn't + * access it. + */ + positive = true; + break; + } + } else { + if (this->d_inode) { + positive = !ovl_is_whiteout(this); + done = true; + } + dput(this); + } + } + + return positive; +} + struct file *ovl_path_open(struct path *path, int flags) { return dentry_open(path, flags | O_NOATIME, current_cred());