namei: path_init() calling conventions change
authorAl Viro <viro@zeniv.linux.org.uk>
Fri, 8 May 2015 21:19:59 +0000 (17:19 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 11 May 2015 12:10:41 +0000 (08:10 -0400)
* lift link_path_walk() into callers; moving it down into path_init()
had been a mistake.  Stack footprint, among other things...
* do _not_ call path_cleanup() after path_init() failure; on all failure
exits out of it we have nothing for path_cleanup() to do
* have path_init() return pathname or ERR_PTR(-E...)

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/namei.c

index 497d5f4163b6249fc352c13bc3a14dce45a06d8c..06c71200be4839680a1817deb0be8a2972771ffa 100644 (file)
@@ -1821,11 +1821,11 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                } while (unlikely(*name == '/'));
                if (unlikely(!*name)) {
 OK:
-                       /* called from path_init(), done */
+                       /* pathname body, done */
                        if (!nd->depth)
                                return 0;
                        name = nd->stack[nd->depth - 1].name;
-                       /* called from trailing_symlink(), done */
+                       /* trailing symlink, done */
                        if (!name)
                                return 0;
                        /* last component of nested symlink */
@@ -1862,8 +1862,8 @@ OK:
        return err;
 }
 
-static int path_init(int dfd, const struct filename *name, unsigned int flags,
-                    struct nameidata *nd)
+static const char *path_init(int dfd, const struct filename *name,
+                            unsigned int flags, struct nameidata *nd)
 {
        int retval = 0;
        const char *s = name->name;
@@ -1871,15 +1871,16 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags,
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
        nd->depth = 0;
+       nd->total_link_count = 0;
        if (flags & LOOKUP_ROOT) {
                struct dentry *root = nd->root.dentry;
                struct inode *inode = root->d_inode;
                if (*s) {
                        if (!d_can_lookup(root))
-                               return -ENOTDIR;
+                               return ERR_PTR(-ENOTDIR);
                        retval = inode_permission(inode, MAY_EXEC);
                        if (retval)
-                               return retval;
+                               return ERR_PTR(retval);
                }
                nd->path = nd->root;
                nd->inode = inode;
@@ -1890,7 +1891,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags,
                } else {
                        path_get(&nd->path);
                }
-               goto done;
+               return s;
        }
 
        nd->root.mnt = NULL;
@@ -1926,14 +1927,14 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags,
                struct dentry *dentry;
 
                if (!f.file)
-                       return -EBADF;
+                       return ERR_PTR(-EBADF);
 
                dentry = f.file->f_path.dentry;
 
                if (*s) {
                        if (!d_can_lookup(dentry)) {
                                fdput(f);
-                               return -ENOTDIR;
+                               return ERR_PTR(-ENOTDIR);
                        }
                }
 
@@ -1947,21 +1948,18 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags,
                        nd->inode = nd->path.dentry->d_inode;
                }
                fdput(f);
-               goto done;
+               return s;
        }
 
        nd->inode = nd->path.dentry->d_inode;
        if (!(flags & LOOKUP_RCU))
-               goto done;
+               return s;
        if (likely(!read_seqcount_retry(&nd->path.dentry->d_seq, nd->seq)))
-               goto done;
+               return s;
        if (!(nd->flags & LOOKUP_ROOT))
                nd->root.mnt = NULL;
        rcu_read_unlock();
-       return -ECHILD;
-done:
-       nd->total_link_count = 0;
-       return link_path_walk(s, nd);
+       return ERR_PTR(-ECHILD);
 }
 
 static void path_cleanup(struct nameidata *nd)
@@ -2014,23 +2012,12 @@ static inline int lookup_last(struct nameidata *nd)
 static int path_lookupat(int dfd, const struct filename *name,
                                unsigned int flags, struct nameidata *nd)
 {
+       const char *s = path_init(dfd, name, flags, nd);
        int err;
 
-       /*
-        * Path walking is largely split up into 2 different synchronisation
-        * schemes, rcu-walk and ref-walk (explained in
-        * Documentation/filesystems/path-lookup.txt). These share much of the
-        * path walk code, but some things particularly setup, cleanup, and
-        * following mounts are sufficiently divergent that functions are
-        * duplicated. Typically there is a function foo(), and its RCU
-        * analogue, foo_rcu().
-        *
-        * -ECHILD is the error number of choice (just to avoid clashes) that
-        * is returned if some aspect of an rcu-walk fails. Such an error must
-        * be handled by restarting a traditional ref-walk (which will always
-        * be able to complete).
-        */
-       err = path_init(dfd, name, flags, nd);
+       if (IS_ERR(s))
+               return PTR_ERR(s);
+       err = link_path_walk(s, nd);
        if (!err) {
                while ((err = lookup_last(nd)) > 0) {
                        err = trailing_symlink(nd);
@@ -2075,7 +2062,11 @@ static int filename_lookup(int dfd, struct filename *name,
 static int path_parentat(int dfd, const struct filename *name,
                                unsigned int flags, struct nameidata *nd)
 {
-       int err = path_init(dfd, name, flags | LOOKUP_PARENT, nd);
+       const char *s = path_init(dfd, name, flags, nd);
+       int err;
+       if (IS_ERR(s))
+               return PTR_ERR(s);
+       err = link_path_walk(s, nd);
        if (!err)
                err = complete_walk(nd);
        path_cleanup(nd);
@@ -2406,7 +2397,11 @@ static int
 path_mountpoint(int dfd, const struct filename *name, struct path *path,
                struct nameidata *nd, unsigned int flags)
 {
-       int err = path_init(dfd, name, flags, nd);
+       const char *s = path_init(dfd, name, flags, nd);
+       int err;
+       if (IS_ERR(s))
+               return PTR_ERR(s);
+       err = link_path_walk(s, nd);
        if (unlikely(err))
                goto out;
 
@@ -3266,6 +3261,7 @@ out:
 static struct file *path_openat(int dfd, struct filename *pathname,
                struct nameidata *nd, const struct open_flags *op, int flags)
 {
+       const char *s;
        struct file *file;
        int opened = 0;
        int error;
@@ -3281,7 +3277,12 @@ static struct file *path_openat(int dfd, struct filename *pathname,
                goto out2;
        }
 
-       error = path_init(dfd, pathname, flags, nd);
+       s = path_init(dfd, pathname, flags, nd);
+       if (IS_ERR(s)) {
+               put_filp(file);
+               return ERR_CAST(s);
+       }
+       error = link_path_walk(s, nd);
        if (unlikely(error))
                goto out;