diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/overlayfs/dir.c | 4 | ||||
-rw-r--r-- | fs/overlayfs/overlayfs.h | 1 | ||||
-rw-r--r-- | fs/overlayfs/super.c | 54 |
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) 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 51e7d58276ce..2bd933aa622b 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 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, 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 @@ out: 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()); |