ext4: support RENAME_WHITEOUT
[firefly-linux-kernel-4.4.55.git] / fs / namespace.c
index abd3abb52616e34085ca33f9db53dd41c03e5938..5b66b2b3624d0da0efe405a6ecb4c86e07a0b58f 100644 (file)
@@ -1439,6 +1439,8 @@ static int do_umount(struct mount *mnt, int flags)
                 * Special case for "unmounting" root ...
                 * we just try to remount it readonly.
                 */
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
                down_write(&sb->s_umount);
                if (!(sb->s_flags & MS_RDONLY))
                        retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
@@ -1684,6 +1686,33 @@ void drop_collected_mounts(struct vfsmount *mnt)
        namespace_unlock();
 }
 
+/**
+ * clone_private_mount - create a private clone of a path
+ *
+ * This creates a new vfsmount, which will be the clone of @path.  The new will
+ * not be attached anywhere in the namespace and will be private (i.e. changes
+ * to the originating mount won't be propagated into this).
+ *
+ * Release with mntput().
+ */
+struct vfsmount *clone_private_mount(struct path *path)
+{
+       struct mount *old_mnt = real_mount(path->mnt);
+       struct mount *new_mnt;
+
+       if (IS_MNT_UNBINDABLE(old_mnt))
+               return ERR_PTR(-EINVAL);
+
+       down_read(&namespace_sem);
+       new_mnt = clone_mnt(old_mnt, path->dentry, CL_PRIVATE);
+       up_read(&namespace_sem);
+       if (IS_ERR(new_mnt))
+               return ERR_CAST(new_mnt);
+
+       return &new_mnt->mnt;
+}
+EXPORT_SYMBOL_GPL(clone_private_mount);
+
 int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
                   struct vfsmount *root)
 {
@@ -2533,7 +2562,7 @@ char *copy_mount_string(const void __user *data)
  * Therefore, if this magic number is present, it carries no information
  * and must be discarded.
  */
-long do_mount(const char *dev_name, const char *dir_name,
+long do_mount(const char *dev_name, const char __user *dir_name,
                const char *type_page, unsigned long flags, void *data_page)
 {
        struct path path;
@@ -2545,15 +2574,11 @@ long do_mount(const char *dev_name, const char *dir_name,
                flags &= ~MS_MGC_MSK;
 
        /* Basic sanity checks */
-
-       if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
-               return -EINVAL;
-
        if (data_page)
                ((char *)data_page)[PAGE_SIZE - 1] = 0;
 
        /* ... and get the mountpoint */
-       retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
+       retval = user_path(dir_name, &path);
        if (retval)
                return retval;
 
@@ -2778,7 +2803,6 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
 {
        int ret;
        char *kernel_type;
-       struct filename *kernel_dir;
        char *kernel_dev;
        unsigned long data_page;
 
@@ -2787,12 +2811,6 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
        if (IS_ERR(kernel_type))
                goto out_type;
 
-       kernel_dir = getname(dir_name);
-       if (IS_ERR(kernel_dir)) {
-               ret = PTR_ERR(kernel_dir);
-               goto out_dir;
-       }
-
        kernel_dev = copy_mount_string(dev_name);
        ret = PTR_ERR(kernel_dev);
        if (IS_ERR(kernel_dev))
@@ -2802,15 +2820,13 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
        if (ret < 0)
                goto out_data;
 
-       ret = do_mount(kernel_dev, kernel_dir->name, kernel_type, flags,
+       ret = do_mount(kernel_dev, dir_name, kernel_type, flags,
                (void *) data_page);
 
        free_page(data_page);
 out_data:
        kfree(kernel_dev);
 out_dev:
-       putname(kernel_dir);
-out_dir:
        kfree(kernel_type);
 out_type:
        return ret;
@@ -2926,6 +2942,9 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
        /* make sure we can reach put_old from new_root */
        if (!is_path_reachable(old_mnt, old.dentry, &new))
                goto out4;
+       /* make certain new is below the root */
+       if (!is_path_reachable(new_mnt, new.dentry, &root))
+               goto out4;
        root_mp->m_count++; /* pin it so it won't go away */
        lock_mount_hash();
        detach_mnt(new_mnt, &parent_path);