summaryrefslogtreecommitdiff
path: root/fs/namei.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2011-03-06 06:58:25 +0300
committerAl Viro <viro@zeniv.linux.org.uk>2011-03-14 16:15:26 +0300
commitfe2d35ff0d18a2c93993b0d7d46f846ff4331b72 (patch)
tree0a60fd245b1aff56a0b1470cadd6e242bfd4a2de /fs/namei.c
parent70e9b3571107b88674cd55ae4bed33f76261e7d3 (diff)
downloadlinux-fe2d35ff0d18a2c93993b0d7d46f846ff4331b72.tar.xz
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: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c100
1 files changed, 67 insertions, 33 deletions
diff --git a/fs/namei.c b/fs/namei.c
index a260a306daf5..9595b4a55c39 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2178,13 +2178,14 @@ exit:
}
/*
- * 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,
const struct open_flags *op, const char *pathname)
{
struct dentry *dir = nd->path.dentry;
struct file *filp;
+ struct inode *inode;
int error;
nd->flags &= ~LOOKUP_PARENT;
@@ -2192,17 +2193,27 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
switch (nd->last_type) {
case LAST_DOTDOT:
- follow_dotdot(nd);
- dir = nd->path.dentry;
case LAST_DOT:
+ error = handle_dots(nd, nd->last_type);
+ if (error)
+ return ERR_PTR(error);
/* fallthrough */
case LAST_ROOT:
+ if (nd->flags & LOOKUP_RCU) {
+ if (nameidata_drop_rcu_last(nd))
+ return ERR_PTR(-ECHILD);
+ }
error = handle_reval_path(nd);
if (error)
goto exit;
- error = -EISDIR;
- goto exit;
+ audit_inode(pathname, nd->path.dentry);
+ if (op->open_flag & O_CREAT) {
+ error = -EISDIR;
+ goto exit;
+ }
+ goto ok;
case LAST_BIND:
+ /* can't be RCU mode here */
error = handle_reval_path(nd);
if (error)
goto exit;
@@ -2210,6 +2221,51 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
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;
/* trailing slashes? */
if (nd->last.name[nd->last.len])
@@ -2303,6 +2359,7 @@ exit:
static struct file *path_openat(int dfd, const char *pathname,
const struct open_flags *op, int flags)
{
+ struct file *base = NULL;
struct file *filp;
struct nameidata nd;
struct path path;
@@ -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.create_mode = op->mode;
- if (op->open_flag & O_CREAT)
- goto creat;
-
- /* !O_CREAT, simple open */
- error = path_lookupat(dfd, pathname, flags | op->intent, &nd);
+ error = path_init(dfd, pathname, flags | LOOKUP_PARENT, &nd, &base);
if (unlikely(error))
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:
- /* OK, have to create the file. Find the parent. */
- error = path_lookupat(dfd, pathname, LOOKUP_PARENT | flags, &nd);
+ current->total_link_count = 0;
+ error = link_path_walk(pathname, &nd);
if (unlikely(error))
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);
while (unlikely(!filp)) { /* trailing symlink */
struct path link = path;
@@ -2386,12 +2419,13 @@ creat:
out:
if (nd.root.mnt)
path_put(&nd.root);
+ if (base)
+ fput(base);
release_open_intent(&nd);
return filp;
exit_dput:
path_put_conditional(&path, &nd);
-out_path:
path_put(&nd.path);
out_filp:
filp = ERR_PTR(error);