summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2011-03-13 19:58:58 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2011-03-15 17:16:20 -0400
commitce57dfc1791221ef58b6d6b8f5437fccefc4e187 (patch)
treef4745174c126231bbd2c4a652d37f086ad035e3c
parent11a7b371b64ef39fc5fb1b6f2218eef7c4d035e3 (diff)
downloadlinux-3.10-ce57dfc1791221ef58b6d6b8f5437fccefc4e187.tar.gz
linux-3.10-ce57dfc1791221ef58b6d6b8f5437fccefc4e187.tar.bz2
linux-3.10-ce57dfc1791221ef58b6d6b8f5437fccefc4e187.zip
pull handling of one pathname component into a helper
new helper: walk_component(). Handles everything except symlinks; returns negative on error, 0 on success and 1 on symlinks we decided to follow. Drops out of RCU mode on such symlinks. link_path_walk() and do_last() switched to using that. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/namei.c123
1 files changed, 55 insertions, 68 deletions
diff --git a/fs/namei.c b/fs/namei.c
index c9b7f5b7e92..549bbe2f25c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -785,16 +785,11 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
* Without that kind of total limit, nasty chains of consecutive
* symlinks can cause almost arbitrarily long lookups.
*/
-static inline int do_follow_link(struct inode *inode, struct path *path, struct nameidata *nd)
+static inline int do_follow_link(struct path *path, struct nameidata *nd)
{
void *cookie;
int err = -ELOOP;
- /* We drop rcu-walk here */
- if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
- return -ECHILD;
- BUG_ON(inode != path->dentry->d_inode);
-
if (current->link_count >= MAX_NESTED_LINKS)
goto loop;
if (current->total_link_count >= 40)
@@ -1337,6 +1332,39 @@ static void terminate_walk(struct nameidata *nd)
}
}
+static inline int walk_component(struct nameidata *nd, struct path *path,
+ struct qstr *name, int type, int follow)
+{
+ struct inode *inode;
+ int err;
+ /*
+ * "." and ".." are special - ".." especially so because it has
+ * to be able to know about the current root directory and
+ * parent relationships.
+ */
+ if (unlikely(type != LAST_NORM))
+ return handle_dots(nd, type);
+ err = do_lookup(nd, name, path, &inode);
+ if (unlikely(err)) {
+ terminate_walk(nd);
+ return err;
+ }
+ if (!inode) {
+ path_to_nameidata(path, nd);
+ terminate_walk(nd);
+ return -ENOENT;
+ }
+ if (unlikely(inode->i_op->follow_link) && follow) {
+ if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
+ return -ECHILD;
+ BUG_ON(inode != path->dentry->d_inode);
+ return 1;
+ }
+ path_to_nameidata(path, nd);
+ nd->inode = inode;
+ return 0;
+}
+
/*
* Name resolution.
* This is the basic name resolution function, turning a pathname into
@@ -1361,7 +1389,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
/* At this point we know we have a real path component. */
for(;;) {
- struct inode *inode;
unsigned long hash;
struct qstr this;
unsigned int c;
@@ -1414,34 +1441,16 @@ static int link_path_walk(const char *name, struct nameidata *nd)
if (!*name)
goto last_with_slashes;
- /*
- * "." and ".." are special - ".." especially so because it has
- * to be able to know about the current root directory and
- * parent relationships.
- */
- if (unlikely(type != LAST_NORM)) {
- if (handle_dots(nd, type))
- return -ECHILD;
- continue;
- }
-
- /* This does the actual lookups.. */
- err = do_lookup(nd, &this, &next, &inode);
- if (err)
- break;
+ err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW);
+ if (err < 0)
+ return err;
- if (inode && inode->i_op->follow_link) {
- err = do_follow_link(inode, &next, nd);
+ if (err) {
+ err = do_follow_link(&next, nd);
if (err)
return err;
nd->inode = nd->path.dentry->d_inode;
- } else {
- path_to_nameidata(&next, nd);
- nd->inode = inode;
}
- err = -ENOENT;
- if (!nd->inode)
- break;
err = -ENOTDIR;
if (!nd->inode->i_op->lookup)
break;
@@ -1453,36 +1462,27 @@ last_with_slashes:
last_component:
/* Clear LOOKUP_CONTINUE iff it was previously unset */
nd->flags &= lookup_flags | ~LOOKUP_CONTINUE;
- if (lookup_flags & LOOKUP_PARENT)
- goto lookup_parent;
- if (unlikely(type != LAST_NORM))
- return handle_dots(nd, type);
- err = do_lookup(nd, &this, &next, &inode);
- if (err)
- break;
- if (inode && unlikely(inode->i_op->follow_link) &&
- (lookup_flags & LOOKUP_FOLLOW)) {
- err = do_follow_link(inode, &next, nd);
+ if (lookup_flags & LOOKUP_PARENT) {
+ nd->last = this;
+ nd->last_type = type;
+ return 0;
+ }
+ err = walk_component(nd, &next, &this, type,
+ lookup_flags & LOOKUP_FOLLOW);
+ if (err < 0)
+ return err;
+ if (err) {
+ err = do_follow_link(&next, nd);
if (err)
return err;
nd->inode = nd->path.dentry->d_inode;
- } else {
- path_to_nameidata(&next, nd);
- nd->inode = inode;
}
- err = -ENOENT;
- if (!nd->inode)
- break;
if (lookup_flags & LOOKUP_DIRECTORY) {
err = -ENOTDIR;
if (!nd->inode->i_op->lookup)
break;
}
return 0;
-lookup_parent:
- nd->last = this;
- nd->last_type = type;
- return 0;
}
terminate_walk(nd);
return err;
@@ -2068,7 +2068,6 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
int want_write = 0;
int acc_mode = op->acc_mode;
struct file *filp;
- struct inode *inode;
int error;
nd->flags &= ~LOOKUP_PARENT;
@@ -2111,24 +2110,12 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))
symlink_ok = 1;
/* we _can_ be in RCU mode here */
- error = do_lookup(nd, &nd->last, path, &inode);
- if (error) {
- terminate_walk(nd);
+ error = walk_component(nd, path, &nd->last, LAST_NORM,
+ !symlink_ok);
+ if (error < 0)
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 && !symlink_ok)) {
- /* We drop rcu-walk here */
- if (nameidata_dentry_drop_rcu_maybe(nd, path->dentry))
- return ERR_PTR(-ECHILD);
+ if (error) /* symlink */
return NULL;
- }
- path_to_nameidata(path, nd);
- nd->inode = inode;
/* sayonara */
if (nd->flags & LOOKUP_RCU) {
if (nameidata_drop_rcu_last(nd))
@@ -2137,7 +2124,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
error = -ENOTDIR;
if (nd->flags & LOOKUP_DIRECTORY) {
- if (!inode->i_op->lookup)
+ if (!nd->inode->i_op->lookup)
goto exit;
}
audit_inode(pathname, nd->path.dentry);