Merge branch 'work.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / fs / namei.c
index e99e2a9..78443a8 100644 (file)
@@ -568,8 +568,8 @@ static bool path_connected(struct vfsmount *mnt, struct dentry *dentry)
 {
        struct super_block *sb = mnt->mnt_sb;
 
-       /* Bind mounts and multi-root filesystems can have disconnected paths */
-       if (!(sb->s_iflags & SB_I_MULTIROOT) && (mnt->mnt_root == sb->s_root))
+       /* Bind mounts can have disconnected paths */
+       if (mnt->mnt_root == sb->s_root)
                return true;
 
        return is_subdir(dentry, mnt->mnt_root);
@@ -1626,7 +1626,8 @@ static const char *pick_link(struct nameidata *nd, struct path *link,
                        return ERR_PTR(error);
        }
 
-       if (unlikely(nd->flags & LOOKUP_NO_SYMLINKS))
+       if (unlikely(nd->flags & LOOKUP_NO_SYMLINKS) ||
+                       unlikely(link->mnt->mnt_flags & MNT_NOSYMFOLLOW))
                return ERR_PTR(-ELOOP);
 
        if (!(nd->flags & LOOKUP_RCU)) {
@@ -2113,8 +2114,10 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                return PTR_ERR(name);
        while (*name=='/')
                name++;
-       if (!*name)
+       if (!*name) {
+               nd->dir_mode = 0; // short-circuit the 'hardening' idiocy
                return 0;
+       }
 
        /* At this point we know we have a real path component. */
        for(;;) {
@@ -4345,8 +4348,8 @@ out:
 }
 EXPORT_SYMBOL(vfs_rename);
 
-static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
-                       const char __user *newname, unsigned int flags)
+int do_renameat2(int olddfd, struct filename *from, int newdfd,
+                struct filename *to, unsigned int flags)
 {
        struct dentry *old_dentry, *new_dentry;
        struct dentry *trap;
@@ -4354,32 +4357,30 @@ static int do_renameat2(int olddfd, const char __user *oldname, int newdfd,
        struct qstr old_last, new_last;
        int old_type, new_type;
        struct inode *delegated_inode = NULL;
-       struct filename *from;
-       struct filename *to;
        unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET;
        bool should_retry = false;
-       int error;
+       int error = -EINVAL;
 
        if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
-               return -EINVAL;
+               goto put_both;
 
        if ((flags & (RENAME_NOREPLACE | RENAME_WHITEOUT)) &&
            (flags & RENAME_EXCHANGE))
-               return -EINVAL;
+               goto put_both;
 
        if (flags & RENAME_EXCHANGE)
                target_flags = 0;
 
 retry:
-       from = filename_parentat(olddfd, getname(oldname), lookup_flags,
-                               &old_path, &old_last, &old_type);
+       from = filename_parentat(olddfd, from, lookup_flags, &old_path,
+                                       &old_last, &old_type);
        if (IS_ERR(from)) {
                error = PTR_ERR(from);
-               goto exit;
+               goto put_new;
        }
 
-       to = filename_parentat(newdfd, getname(newname), lookup_flags,
-                               &new_path, &new_last, &new_type);
+       to = filename_parentat(newdfd, to, lookup_flags, &new_path, &new_last,
+                               &new_type);
        if (IS_ERR(to)) {
                error = PTR_ERR(to);
                goto exit1;
@@ -4472,34 +4473,40 @@ exit2:
        if (retry_estale(error, lookup_flags))
                should_retry = true;
        path_put(&new_path);
-       putname(to);
 exit1:
        path_put(&old_path);
-       putname(from);
        if (should_retry) {
                should_retry = false;
                lookup_flags |= LOOKUP_REVAL;
                goto retry;
        }
-exit:
+put_both:
+       if (!IS_ERR(from))
+               putname(from);
+put_new:
+       if (!IS_ERR(to))
+               putname(to);
        return error;
 }
 
 SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
                int, newdfd, const char __user *, newname, unsigned int, flags)
 {
-       return do_renameat2(olddfd, oldname, newdfd, newname, flags);
+       return do_renameat2(olddfd, getname(oldname), newdfd, getname(newname),
+                               flags);
 }
 
 SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
                int, newdfd, const char __user *, newname)
 {
-       return do_renameat2(olddfd, oldname, newdfd, newname, 0);
+       return do_renameat2(olddfd, getname(oldname), newdfd, getname(newname),
+                               0);
 }
 
 SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
 {
-       return do_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
+       return do_renameat2(AT_FDCWD, getname(oldname), AT_FDCWD,
+                               getname(newname), 0);
 }
 
 int readlink_copy(char __user *buffer, int buflen, const char *link)