Commit e28edc46 authored by Miklos Szeredi's avatar Miklos Szeredi

ovl: consolidate lookup for underlying layers

Use a common helper for lookup of upper and lower layers.  This paves the
way for looking up directory redirects.

No functional change.
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 48fab5d7
...@@ -13,26 +13,13 @@ ...@@ -13,26 +13,13 @@
#include "overlayfs.h" #include "overlayfs.h"
#include "ovl_entry.h" #include "ovl_entry.h"
static struct dentry *ovl_lookup_real(struct dentry *dir, struct ovl_lookup_data {
const struct qstr *name) struct qstr name;
{ bool is_dir;
struct dentry *dentry; bool opaque;
bool stop;
dentry = lookup_one_len_unlocked(name->name, dir, name->len); bool last;
if (IS_ERR(dentry)) { };
if (PTR_ERR(dentry) == -ENOENT ||
PTR_ERR(dentry) == -ENAMETOOLONG)
dentry = NULL;
} else if (!dentry->d_inode) {
dput(dentry);
dentry = NULL;
} else if (ovl_dentry_weird(dentry)) {
dput(dentry);
/* Don't support traversing automounts and other weirdness */
dentry = ERR_PTR(-EREMOTE);
}
return dentry;
}
static bool ovl_is_opaquedir(struct dentry *dentry) static bool ovl_is_opaquedir(struct dentry *dentry)
{ {
...@@ -49,6 +36,64 @@ static bool ovl_is_opaquedir(struct dentry *dentry) ...@@ -49,6 +36,64 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
return false; return false;
} }
static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
const char *name, unsigned int namelen,
struct dentry **ret)
{
struct dentry *this;
int err;
this = lookup_one_len_unlocked(name, base, namelen);
if (IS_ERR(this)) {
err = PTR_ERR(this);
this = NULL;
if (err == -ENOENT || err == -ENAMETOOLONG)
goto out;
goto out_err;
}
if (!this->d_inode)
goto put_and_out;
if (ovl_dentry_weird(this)) {
/* Don't support traversing automounts and other weirdness */
err = -EREMOTE;
goto out_err;
}
if (ovl_is_whiteout(this)) {
d->stop = d->opaque = true;
goto put_and_out;
}
if (!d_can_lookup(this)) {
d->stop = true;
if (d->is_dir)
goto put_and_out;
goto out;
}
d->is_dir = true;
if (!d->last && ovl_is_opaquedir(this)) {
d->stop = d->opaque = true;
goto out;
}
out:
*ret = this;
return 0;
put_and_out:
dput(this);
this = NULL;
goto out;
out_err:
dput(this);
return err;
}
static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
struct dentry **ret)
{
return ovl_lookup_single(base, d, d->name.name, d->name.len, ret);
}
/* /*
* Returns next layer in stack starting from top. * Returns next layer in stack starting from top.
* Returns -1 if this is the last layer. * Returns -1 if this is the last layer.
...@@ -82,11 +127,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -82,11 +127,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int ctr = 0; unsigned int ctr = 0;
struct inode *inode = NULL; struct inode *inode = NULL;
bool upperopaque = false; bool upperopaque = false;
bool stop = false;
bool isdir = false;
struct dentry *this; struct dentry *this;
unsigned int i; unsigned int i;
int err; int err;
struct ovl_lookup_data d = {
.name = dentry->d_name,
.is_dir = false,
.opaque = false,
.stop = false,
.last = !poe->numlower,
};
if (dentry->d_name.len > ofs->namelen) if (dentry->d_name.len > ofs->namelen)
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
...@@ -94,70 +144,36 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -94,70 +144,36 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
upperdir = ovl_upperdentry_dereference(poe); upperdir = ovl_upperdentry_dereference(poe);
if (upperdir) { if (upperdir) {
this = ovl_lookup_real(upperdir, &dentry->d_name); err = ovl_lookup_layer(upperdir, &d, &upperdentry);
err = PTR_ERR(this); if (err)
if (IS_ERR(this))
goto out; goto out;
if (this) { if (upperdentry && unlikely(ovl_dentry_remote(upperdentry))) {
if (unlikely(ovl_dentry_remote(this))) { dput(upperdentry);
dput(this); err = -EREMOTE;
err = -EREMOTE; goto out;
goto out;
}
if (ovl_is_whiteout(this)) {
dput(this);
this = NULL;
stop = upperopaque = true;
} else if (!d_is_dir(this)) {
stop = true;
} else {
isdir = true;
if (poe->numlower && ovl_is_opaquedir(this))
stop = upperopaque = true;
}
} }
upperdentry = this; upperopaque = d.opaque;
} }
if (!stop && poe->numlower) { if (!d.stop && poe->numlower) {
err = -ENOMEM; err = -ENOMEM;
stack = kcalloc(poe->numlower, sizeof(struct path), GFP_KERNEL); stack = kcalloc(poe->numlower, sizeof(struct path),
GFP_TEMPORARY);
if (!stack) if (!stack)
goto out_put_upper; goto out_put_upper;
} }
for (i = 0; !stop && i < poe->numlower; i++) { for (i = 0; !d.stop && i < poe->numlower; i++) {
struct path lowerpath = poe->lowerstack[i]; struct path lowerpath = poe->lowerstack[i];
this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name); d.last = i == poe->numlower - 1;
err = PTR_ERR(this); err = ovl_lookup_layer(lowerpath.dentry, &d, &this);
if (IS_ERR(this)) if (err)
goto out_put; goto out_put;
if (!this) if (!this)
continue; continue;
if (ovl_is_whiteout(this)) {
dput(this);
break;
}
/*
* If this is a non-directory then stop here.
*/
if (!d_is_dir(this)) {
if (isdir) {
dput(this);
break;
}
stop = true;
} else {
/*
* Only makes sense to check opaque dir if this is not
* the lowermost layer.
*/
if (i < poe->numlower - 1 && ovl_is_opaquedir(this))
stop = true;
}
stack[ctr].dentry = this; stack[ctr].dentry = this;
stack[ctr].mnt = lowerpath.mnt; stack[ctr].mnt = lowerpath.mnt;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment