Commit fe2d35ff authored by Al Viro's avatar Al Viro

switch non-create side of open() to use of do_last()

Instead of path_lookupat() doing trailing symlink resolution,
use the same scheme as on the O_CREAT side.  Walk with
LOOKUP_PARENT, then (in do_last()) look the final component
up, then either open it or return error or, if it's a symlink,
give the symlink back to path_openat() to be resolved there.

The really messy complication here is RCU.  We don't want to drop
out of RCU mode before the final lookup, since we don't want to
bounce parent directory ->d_count without a good reason.

Result is _not_ pretty; later in the series we'll clean it up.
For now we are roughly back where we'd been before the revert
done by Nick's series - top-level logics of path_openat() is
cleaned up, do_last() does actual opening, symlink resolution is
done uniformly.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 70e9b357
...@@ -2178,13 +2178,14 @@ static struct file *finish_open(struct nameidata *nd, ...@@ -2178,13 +2178,14 @@ static struct file *finish_open(struct nameidata *nd,
} }
/* /*
* Handle O_CREAT case for do_filp_open * Handle the last step of open()
*/ */
static struct file *do_last(struct nameidata *nd, struct path *path, static struct file *do_last(struct nameidata *nd, struct path *path,
const struct open_flags *op, const char *pathname) const struct open_flags *op, const char *pathname)
{ {
struct dentry *dir = nd->path.dentry; struct dentry *dir = nd->path.dentry;
struct file *filp; struct file *filp;
struct inode *inode;
int error; int error;
nd->flags &= ~LOOKUP_PARENT; nd->flags &= ~LOOKUP_PARENT;
...@@ -2192,17 +2193,27 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2192,17 +2193,27 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
switch (nd->last_type) { switch (nd->last_type) {
case LAST_DOTDOT: case LAST_DOTDOT:
follow_dotdot(nd);
dir = nd->path.dentry;
case LAST_DOT: case LAST_DOT:
error = handle_dots(nd, nd->last_type);
if (error)
return ERR_PTR(error);
/* fallthrough */ /* fallthrough */
case LAST_ROOT: case LAST_ROOT:
if (nd->flags & LOOKUP_RCU) {
if (nameidata_drop_rcu_last(nd))
return ERR_PTR(-ECHILD);
}
error = handle_reval_path(nd); error = handle_reval_path(nd);
if (error) if (error)
goto exit; goto exit;
error = -EISDIR; audit_inode(pathname, nd->path.dentry);
goto exit; if (op->open_flag & O_CREAT) {
error = -EISDIR;
goto exit;
}
goto ok;
case LAST_BIND: case LAST_BIND:
/* can't be RCU mode here */
error = handle_reval_path(nd); error = handle_reval_path(nd);
if (error) if (error)
goto exit; goto exit;
...@@ -2210,6 +2221,51 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2210,6 +2221,51 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
goto ok; goto ok;
} }
if (!(op->open_flag & O_CREAT)) {
if (nd->last.name[nd->last.len])
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
/* we _can_ be in RCU mode here */
error = do_lookup(nd, &nd->last, path, &inode);
if (error) {
terminate_walk(nd);
return ERR_PTR(error);
}
if (!inode) {
path_to_nameidata(path, nd);
terminate_walk(nd);
return ERR_PTR(-ENOENT);
}
if (unlikely(inode->i_op->follow_link)) {
/* We drop rcu-walk here */
if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
return ERR_PTR(-ECHILD);
return NULL;
}
path_to_nameidata(path, nd);
nd->inode = inode;
/* sayonara */
if (nd->flags & LOOKUP_RCU) {
if (nameidata_drop_rcu_last(nd))
return ERR_PTR(-ECHILD);
}
error = -ENOTDIR;
if (nd->flags & LOOKUP_DIRECTORY) {
if (!inode->i_op->lookup)
goto exit;
}
audit_inode(pathname, nd->path.dentry);
goto ok;
}
/* create side of things */
if (nd->flags & LOOKUP_RCU) {
if (nameidata_drop_rcu_last(nd))
return ERR_PTR(-ECHILD);
}
audit_inode(pathname, dir);
error = -EISDIR; error = -EISDIR;
/* trailing slashes? */ /* trailing slashes? */
if (nd->last.name[nd->last.len]) if (nd->last.name[nd->last.len])
...@@ -2303,6 +2359,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, ...@@ -2303,6 +2359,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
static struct file *path_openat(int dfd, const char *pathname, static struct file *path_openat(int dfd, const char *pathname,
const struct open_flags *op, int flags) const struct open_flags *op, int flags)
{ {
struct file *base = NULL;
struct file *filp; struct file *filp;
struct nameidata nd; struct nameidata nd;
struct path path; struct path path;
...@@ -2318,39 +2375,15 @@ static struct file *path_openat(int dfd, const char *pathname, ...@@ -2318,39 +2375,15 @@ static struct file *path_openat(int dfd, const char *pathname,
nd.intent.open.flags = open_to_namei_flags(op->open_flag); nd.intent.open.flags = open_to_namei_flags(op->open_flag);
nd.intent.open.create_mode = op->mode; nd.intent.open.create_mode = op->mode;
if (op->open_flag & O_CREAT) error = path_init(dfd, pathname, flags | LOOKUP_PARENT, &nd, &base);
goto creat;
/* !O_CREAT, simple open */
error = path_lookupat(dfd, pathname, flags | op->intent, &nd);
if (unlikely(error)) if (unlikely(error))
goto out_filp; goto out_filp;
error = -ELOOP;
if (!(nd.flags & LOOKUP_FOLLOW)) {
if (nd.inode->i_op->follow_link)
goto out_path;
}
error = -ENOTDIR;
if (nd.flags & LOOKUP_DIRECTORY) {
if (!nd.inode->i_op->lookup)
goto out_path;
}
audit_inode(pathname, nd.path.dentry);
filp = finish_open(&nd, op->open_flag, op->acc_mode);
release_open_intent(&nd);
return filp;
creat: current->total_link_count = 0;
/* OK, have to create the file. Find the parent. */ error = link_path_walk(pathname, &nd);
error = path_lookupat(dfd, pathname, LOOKUP_PARENT | flags, &nd);
if (unlikely(error)) if (unlikely(error))
goto out_filp; goto out_filp;
if (unlikely(!audit_dummy_context()))
audit_inode(pathname, nd.path.dentry);
/*
* We have the parent and last component.
*/
filp = do_last(&nd, &path, op, pathname); filp = do_last(&nd, &path, op, pathname);
while (unlikely(!filp)) { /* trailing symlink */ while (unlikely(!filp)) { /* trailing symlink */
struct path link = path; struct path link = path;
...@@ -2386,12 +2419,13 @@ static struct file *path_openat(int dfd, const char *pathname, ...@@ -2386,12 +2419,13 @@ static struct file *path_openat(int dfd, const char *pathname,
out: out:
if (nd.root.mnt) if (nd.root.mnt)
path_put(&nd.root); path_put(&nd.root);
if (base)
fput(base);
release_open_intent(&nd); release_open_intent(&nd);
return filp; return filp;
exit_dput: exit_dput:
path_put_conditional(&path, &nd); path_put_conditional(&path, &nd);
out_path:
path_put(&nd.path); path_put(&nd.path);
out_filp: out_filp:
filp = ERR_PTR(error); filp = ERR_PTR(error);
......
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