Get rid of symlink body copying
Al Viro [Sat, 26 Dec 2009 13:37:05 +0000 (08:37 -0500)]
Now that nd->last stays around until ->put_link() is called, we can
just postpone that ->put_link() in do_filp_open() a bit and don't
bother with copying.

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

fs/namei.c

index adfbaf5..1f5d86d 100644 (file)
@@ -498,8 +498,6 @@ static int link_path_walk(const char *, struct nameidata *);
 
 static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
 {
-       int res = 0;
-       char *name;
        if (IS_ERR(link))
                goto fail;
 
@@ -510,22 +508,7 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
                path_get(&nd->root);
        }
 
-       res = link_path_walk(link, nd);
-       if (nd->depth || res || nd->last_type!=LAST_NORM)
-               return res;
-       /*
-        * If it is an iterative symlinks resolution in open_namei() we
-        * have to copy the last component. And all that crap because of
-        * bloody create() on broken symlinks. Furrfu...
-        */
-       name = __getname();
-       if (unlikely(!name)) {
-               path_put(&nd->path);
-               return -ENOMEM;
-       }
-       strcpy(name, nd->last.name);
-       nd->last.name = name;
-       return 0;
+       return link_path_walk(link, nd);
 fail:
        path_put(&nd->path);
        return PTR_ERR(link);
@@ -547,10 +530,10 @@ static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
        nd->path.dentry = path->dentry;
 }
 
-static __always_inline int __do_follow_link(struct path *path, struct nameidata *nd)
+static __always_inline int
+__do_follow_link(struct path *path, struct nameidata *nd, void **p)
 {
        int error;
-       void *cookie;
        struct dentry *dentry = path->dentry;
 
        touch_atime(path->mnt, dentry);
@@ -562,9 +545,9 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
        }
        mntget(path->mnt);
        nd->last_type = LAST_BIND;
-       cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
-       error = PTR_ERR(cookie);
-       if (!IS_ERR(cookie)) {
+       *p = dentry->d_inode->i_op->follow_link(dentry, nd);
+       error = PTR_ERR(*p);
+       if (!IS_ERR(*p)) {
                char *s = nd_get_link(nd);
                error = 0;
                if (s)
@@ -574,8 +557,6 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
                        if (error)
                                path_put(&nd->path);
                }
-               if (dentry->d_inode->i_op->put_link)
-                       dentry->d_inode->i_op->put_link(dentry, nd, cookie);
        }
        return error;
 }
@@ -589,6 +570,7 @@ static __always_inline int __do_follow_link(struct path *path, struct nameidata
  */
 static inline int do_follow_link(struct path *path, struct nameidata *nd)
 {
+       void *cookie;
        int err = -ELOOP;
        if (current->link_count >= MAX_NESTED_LINKS)
                goto loop;
@@ -602,7 +584,9 @@ static inline int do_follow_link(struct path *path, struct nameidata *nd)
        current->link_count++;
        current->total_link_count++;
        nd->depth++;
-       err = __do_follow_link(path, nd);
+       err = __do_follow_link(path, nd, &cookie);
+       if (!IS_ERR(cookie) && path->dentry->d_inode->i_op->put_link)
+               path->dentry->d_inode->i_op->put_link(path->dentry, nd, cookie);
        path_put(path);
        current->link_count--;
        nd->depth--;
@@ -1847,6 +1831,9 @@ reval:
                nd.flags |= LOOKUP_EXCL;
        filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname);
        while (unlikely(!filp)) { /* trailing symlink */
+               struct path holder;
+               struct inode *inode;
+               void *cookie;
                error = -ELOOP;
                if ((open_flag & O_NOFOLLOW) || count++ == 32)
                        goto exit_dput;
@@ -1865,18 +1852,24 @@ reval:
                error = security_inode_follow_link(path.dentry, &nd);
                if (error)
                        goto exit_dput;
-               error = __do_follow_link(&path, &nd);
-               path_put(&path);
-               if (error) {
+               error = __do_follow_link(&path, &nd, &cookie);
+               if (unlikely(error)) {
                        /* nd.path had been dropped */
+                       inode = path.dentry->d_inode;
+                       if (!IS_ERR(cookie) && inode->i_op->put_link)
+                               inode->i_op->put_link(path.dentry, &nd, cookie);
+                       path_put(&path);
                        release_open_intent(&nd);
                        filp = ERR_PTR(error);
                        goto out;
                }
+               holder = path;
                nd.flags &= ~LOOKUP_PARENT;
                filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname);
-               if (nd.last_type == LAST_NORM)
-                       __putname(nd.last.name);
+               inode = holder.dentry->d_inode;
+               if (inode->i_op->put_link)
+                       inode->i_op->put_link(holder.dentry, &nd, cookie);
+               path_put(&holder);
        }
 out:
        if (nd.root.mnt)