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>
This commit is contained in:
parent
c412ce4983
commit
2aff4534b6
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue