c6x: switch to ->regset_get()
[linux-2.6-microblaze.git] / fs / namespace.c
index 85b5f7b..f30ed40 100644 (file)
@@ -648,6 +648,21 @@ struct vfsmount *lookup_mnt(const struct path *path)
        return m;
 }
 
+static inline void lock_ns_list(struct mnt_namespace *ns)
+{
+       spin_lock(&ns->ns_lock);
+}
+
+static inline void unlock_ns_list(struct mnt_namespace *ns)
+{
+       spin_unlock(&ns->ns_lock);
+}
+
+static inline bool mnt_is_cursor(struct mount *mnt)
+{
+       return mnt->mnt.mnt_flags & MNT_CURSOR;
+}
+
 /*
  * __is_local_mountpoint - Test to see if dentry is a mountpoint in the
  *                         current mount namespace.
@@ -669,17 +684,18 @@ bool __is_local_mountpoint(struct dentry *dentry)
        struct mount *mnt;
        bool is_covered = false;
 
-       if (!d_mountpoint(dentry))
-               goto out;
-
        down_read(&namespace_sem);
+       lock_ns_list(ns);
        list_for_each_entry(mnt, &ns->list, mnt_list) {
+               if (mnt_is_cursor(mnt))
+                       continue;
                is_covered = (mnt->mnt_mountpoint == dentry);
                if (is_covered)
                        break;
        }
+       unlock_ns_list(ns);
        up_read(&namespace_sem);
-out:
+
        return is_covered;
 }
 
@@ -1245,46 +1261,71 @@ struct vfsmount *mnt_clone_internal(const struct path *path)
 }
 
 #ifdef CONFIG_PROC_FS
+static struct mount *mnt_list_next(struct mnt_namespace *ns,
+                                  struct list_head *p)
+{
+       struct mount *mnt, *ret = NULL;
+
+       lock_ns_list(ns);
+       list_for_each_continue(p, &ns->list) {
+               mnt = list_entry(p, typeof(*mnt), mnt_list);
+               if (!mnt_is_cursor(mnt)) {
+                       ret = mnt;
+                       break;
+               }
+       }
+       unlock_ns_list(ns);
+
+       return ret;
+}
+
 /* iterator; we want it to have access to namespace_sem, thus here... */
 static void *m_start(struct seq_file *m, loff_t *pos)
 {
        struct proc_mounts *p = m->private;
+       struct list_head *prev;
 
        down_read(&namespace_sem);
-       if (p->cached_event == p->ns->event) {
-               void *v = p->cached_mount;
-               if (*pos == p->cached_index)
-                       return v;
-               if (*pos == p->cached_index + 1) {
-                       v = seq_list_next(v, &p->ns->list, &p->cached_index);
-                       return p->cached_mount = v;
-               }
+       if (!*pos) {
+               prev = &p->ns->list;
+       } else {
+               prev = &p->cursor.mnt_list;
+
+               /* Read after we'd reached the end? */
+               if (list_empty(prev))
+                       return NULL;
        }
 
-       p->cached_event = p->ns->event;
-       p->cached_mount = seq_list_start(&p->ns->list, *pos);
-       p->cached_index = *pos;
-       return p->cached_mount;
+       return mnt_list_next(p->ns, prev);
 }
 
 static void *m_next(struct seq_file *m, void *v, loff_t *pos)
 {
        struct proc_mounts *p = m->private;
+       struct mount *mnt = v;
 
-       p->cached_mount = seq_list_next(v, &p->ns->list, pos);
-       p->cached_index = *pos;
-       return p->cached_mount;
+       ++*pos;
+       return mnt_list_next(p->ns, &mnt->mnt_list);
 }
 
 static void m_stop(struct seq_file *m, void *v)
 {
+       struct proc_mounts *p = m->private;
+       struct mount *mnt = v;
+
+       lock_ns_list(p->ns);
+       if (mnt)
+               list_move_tail(&p->cursor.mnt_list, &mnt->mnt_list);
+       else
+               list_del_init(&p->cursor.mnt_list);
+       unlock_ns_list(p->ns);
        up_read(&namespace_sem);
 }
 
 static int m_show(struct seq_file *m, void *v)
 {
        struct proc_mounts *p = m->private;
-       struct mount *r = list_entry(v, struct mount, mnt_list);
+       struct mount *r = v;
        return p->show(m, &r->mnt);
 }
 
@@ -1294,6 +1335,15 @@ const struct seq_operations mounts_op = {
        .stop   = m_stop,
        .show   = m_show,
 };
+
+void mnt_cursor_del(struct mnt_namespace *ns, struct mount *cursor)
+{
+       down_read(&namespace_sem);
+       lock_ns_list(ns);
+       list_del(&cursor->mnt_list);
+       unlock_ns_list(ns);
+       up_read(&namespace_sem);
+}
 #endif  /* CONFIG_PROC_FS */
 
 /**
@@ -1669,7 +1719,7 @@ int ksys_umount(char __user *name, int flags)
        struct path path;
        struct mount *mnt;
        int retval;
-       int lookup_flags = 0;
+       int lookup_flags = LOOKUP_MOUNTPOINT;
 
        if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
                return -EINVAL;
@@ -1680,7 +1730,7 @@ int ksys_umount(char __user *name, int flags)
        if (!(flags & UMOUNT_NOFOLLOW))
                lookup_flags |= LOOKUP_FOLLOW;
 
-       retval = user_path_mountpoint_at(AT_FDCWD, name, lookup_flags, &path);
+       retval = user_path_at(AT_FDCWD, name, lookup_flags, &path);
        if (retval)
                goto out;
        mnt = real_mount(path.mnt);
@@ -1733,6 +1783,11 @@ static struct mnt_namespace *to_mnt_ns(struct ns_common *ns)
        return container_of(ns, struct mnt_namespace, ns);
 }
 
+struct ns_common *from_mnt_ns(struct mnt_namespace *mnt)
+{
+       return &mnt->ns;
+}
+
 static bool mnt_ns_loop(struct dentry *dentry)
 {
        /* Could bind mounting the mount namespace inode cause a
@@ -1879,6 +1934,9 @@ struct vfsmount *clone_private_mount(const struct path *path)
        if (IS_ERR(new_mnt))
                return ERR_CAST(new_mnt);
 
+       /* Longterm mount to be removed by kern_unmount*() */
+       new_mnt->mnt_ns = MNT_NS_INTERNAL;
+
        return &new_mnt->mnt;
 }
 EXPORT_SYMBOL_GPL(clone_private_mount);
@@ -2697,45 +2755,32 @@ static int do_move_mount_old(struct path *path, const char *old_name)
 /*
  * add a mount into a namespace's mount tree
  */
-static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
+static int do_add_mount(struct mount *newmnt, struct mountpoint *mp,
+                       struct path *path, int mnt_flags)
 {
-       struct mountpoint *mp;
-       struct mount *parent;
-       int err;
+       struct mount *parent = real_mount(path->mnt);
 
        mnt_flags &= ~MNT_INTERNAL_FLAGS;
 
-       mp = lock_mount(path);
-       if (IS_ERR(mp))
-               return PTR_ERR(mp);
-
-       parent = real_mount(path->mnt);
-       err = -EINVAL;
        if (unlikely(!check_mnt(parent))) {
                /* that's acceptable only for automounts done in private ns */
                if (!(mnt_flags & MNT_SHRINKABLE))
-                       goto unlock;
+                       return -EINVAL;
                /* ... and for those we'd better have mountpoint still alive */
                if (!parent->mnt_ns)
-                       goto unlock;
+                       return -EINVAL;
        }
 
        /* Refuse the same filesystem on the same mount point */
-       err = -EBUSY;
        if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb &&
            path->mnt->mnt_root == path->dentry)
-               goto unlock;
+               return -EBUSY;
 
-       err = -EINVAL;
        if (d_is_symlink(newmnt->mnt.mnt_root))
-               goto unlock;
+               return -EINVAL;
 
        newmnt->mnt.mnt_flags = mnt_flags;
-       err = graft_tree(newmnt, parent, mp);
-
-unlock:
-       unlock_mount(mp);
-       return err;
+       return graft_tree(newmnt, parent, mp);
 }
 
 static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags);
@@ -2748,6 +2793,7 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
                           unsigned int mnt_flags)
 {
        struct vfsmount *mnt;
+       struct mountpoint *mp;
        struct super_block *sb = fc->root->d_sb;
        int error;
 
@@ -2768,7 +2814,13 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
 
        mnt_warn_timestamp_expiry(mountpoint, mnt);
 
-       error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags);
+       mp = lock_mount(mountpoint);
+       if (IS_ERR(mp)) {
+               mntput(mnt);
+               return PTR_ERR(mp);
+       }
+       error = do_add_mount(real_mount(mnt), mp, mountpoint, mnt_flags);
+       unlock_mount(mp);
        if (error < 0)
                mntput(mnt);
        return error;
@@ -2829,23 +2881,63 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
 
 int finish_automount(struct vfsmount *m, struct path *path)
 {
-       struct mount *mnt = real_mount(m);
+       struct dentry *dentry = path->dentry;
+       struct mountpoint *mp;
+       struct mount *mnt;
        int err;
+
+       if (!m)
+               return 0;
+       if (IS_ERR(m))
+               return PTR_ERR(m);
+
+       mnt = real_mount(m);
        /* The new mount record should have at least 2 refs to prevent it being
         * expired before we get a chance to add it
         */
        BUG_ON(mnt_get_count(mnt) < 2);
 
        if (m->mnt_sb == path->mnt->mnt_sb &&
-           m->mnt_root == path->dentry) {
+           m->mnt_root == dentry) {
                err = -ELOOP;
-               goto fail;
+               goto discard;
        }
 
-       err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
-       if (!err)
-               return 0;
-fail:
+       /*
+        * we don't want to use lock_mount() - in this case finding something
+        * that overmounts our mountpoint to be means "quitely drop what we've
+        * got", not "try to mount it on top".
+        */
+       inode_lock(dentry->d_inode);
+       namespace_lock();
+       if (unlikely(cant_mount(dentry))) {
+               err = -ENOENT;
+               goto discard_locked;
+       }
+       rcu_read_lock();
+       if (unlikely(__lookup_mnt(path->mnt, dentry))) {
+               rcu_read_unlock();
+               err = 0;
+               goto discard_locked;
+       }
+       rcu_read_unlock();
+       mp = get_mountpoint(dentry);
+       if (IS_ERR(mp)) {
+               err = PTR_ERR(mp);
+               goto discard_locked;
+       }
+
+       err = do_add_mount(mnt, mp, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
+       unlock_mount(mp);
+       if (unlikely(err))
+               goto discard;
+       mntput(m);
+       return 0;
+
+discard_locked:
+       namespace_unlock();
+       inode_unlock(dentry->d_inode);
+discard:
        /* remove m from any expiration list it may be on */
        if (!list_empty(&mnt->mnt_expire)) {
                namespace_lock();
@@ -3168,6 +3260,7 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns, bool a
        atomic_set(&new_ns->count, 1);
        INIT_LIST_HEAD(&new_ns->list);
        init_waitqueue_head(&new_ns->poll);
+       spin_lock_init(&new_ns->ns_lock);
        new_ns->user_ns = get_user_ns(user_ns);
        new_ns->ucounts = ucounts;
        return new_ns;
@@ -3561,7 +3654,7 @@ EXPORT_SYMBOL(path_is_under);
  * file system may be mounted on put_old. After all, new_root is a mountpoint.
  *
  * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
- * See Documentation/filesystems/ramfs-rootfs-initramfs.txt for alternatives
+ * See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
  * in this situation.
  *
  * Notes:
@@ -3770,6 +3863,19 @@ void kern_unmount(struct vfsmount *mnt)
 }
 EXPORT_SYMBOL(kern_unmount);
 
+void kern_unmount_array(struct vfsmount *mnt[], unsigned int num)
+{
+       unsigned int i;
+
+       for (i = 0; i < num; i++)
+               if (mnt[i])
+                       real_mount(mnt[i])->mnt_ns = NULL;
+       synchronize_rcu_expedited();
+       for (i = 0; i < num; i++)
+               mntput(mnt[i]);
+}
+EXPORT_SYMBOL(kern_unmount_array);
+
 bool our_mnt(struct vfsmount *mnt)
 {
        return check_mnt(real_mount(mnt));
@@ -3808,10 +3914,14 @@ static bool mnt_already_visible(struct mnt_namespace *ns,
        bool visible = false;
 
        down_read(&namespace_sem);
+       lock_ns_list(ns);
        list_for_each_entry(mnt, &ns->list, mnt_list) {
                struct mount *child;
                int mnt_flags;
 
+               if (mnt_is_cursor(mnt))
+                       continue;
+
                if (mnt->mnt.mnt_sb->s_type != sb->s_type)
                        continue;
 
@@ -3859,6 +3969,7 @@ static bool mnt_already_visible(struct mnt_namespace *ns,
        next:   ;
        }
 found:
+       unlock_ns_list(ns);
        up_read(&namespace_sem);
        return visible;
 }
@@ -3920,16 +4031,18 @@ static void mntns_put(struct ns_common *ns)
        put_mnt_ns(to_mnt_ns(ns));
 }
 
-static int mntns_install(struct nsproxy *nsproxy, struct ns_common *ns)
+static int mntns_install(struct nsset *nsset, struct ns_common *ns)
 {
-       struct fs_struct *fs = current->fs;
+       struct nsproxy *nsproxy = nsset->nsproxy;
+       struct fs_struct *fs = nsset->fs;
        struct mnt_namespace *mnt_ns = to_mnt_ns(ns), *old_mnt_ns;
+       struct user_namespace *user_ns = nsset->cred->user_ns;
        struct path root;
        int err;
 
        if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) ||
-           !ns_capable(current_user_ns(), CAP_SYS_CHROOT) ||
-           !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
+           !ns_capable(user_ns, CAP_SYS_CHROOT) ||
+           !ns_capable(user_ns, CAP_SYS_ADMIN))
                return -EPERM;
 
        if (is_anon_ns(mnt_ns))