mnt: On an unmount propagate clearing of MNT_LOCKED
[cascardo/linux.git] / fs / namespace.c
index 72a286e..2b12b7a 100644 (file)
@@ -632,14 +632,17 @@ struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
  */
 struct mount *__lookup_mnt_last(struct vfsmount *mnt, struct dentry *dentry)
 {
-       struct mount *p, *res;
-       res = p = __lookup_mnt(mnt, dentry);
+       struct mount *p, *res = NULL;
+       p = __lookup_mnt(mnt, dentry);
        if (!p)
                goto out;
+       if (!(p->mnt.mnt_flags & MNT_UMOUNT))
+               res = p;
        hlist_for_each_entry_continue(p, mnt_hash) {
                if (&p->mnt_parent->mnt != mnt || p->mnt_mountpoint != dentry)
                        break;
-               res = p;
+               if (!(p->mnt.mnt_flags & MNT_UMOUNT))
+                       res = p;
        }
 out:
        return res;
@@ -1298,17 +1301,15 @@ static HLIST_HEAD(unmounted);   /* protected by namespace_sem */
 
 static void namespace_unlock(void)
 {
-       struct hlist_head head = unmounted;
+       struct hlist_head head;
 
-       if (likely(hlist_empty(&head))) {
-               up_write(&namespace_sem);
-               return;
-       }
+       hlist_move_list(&unmounted, &head);
 
-       head.first->pprev = &head.first;
-       INIT_HLIST_HEAD(&unmounted);
        up_write(&namespace_sem);
 
+       if (likely(hlist_empty(&head)))
+               return;
+
        synchronize_rcu();
 
        group_pin_kill(&head);
@@ -1319,37 +1320,44 @@ static inline void namespace_lock(void)
        down_write(&namespace_sem);
 }
 
+enum umount_tree_flags {
+       UMOUNT_SYNC = 1,
+       UMOUNT_PROPAGATE = 2,
+};
 /*
  * mount_lock must be held
  * namespace_sem must be held for write
- * how = 0 => just this tree, don't propagate
- * how = 1 => propagate; we know that nobody else has reference to any victims
- * how = 2 => lazy umount
  */
-void umount_tree(struct mount *mnt, int how)
+static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
 {
-       HLIST_HEAD(tmp_list);
+       LIST_HEAD(tmp_list);
        struct mount *p;
 
+       if (how & UMOUNT_PROPAGATE)
+               propagate_mount_unlock(mnt);
+
+       /* Gather the mounts to umount */
        for (p = mnt; p; p = next_mnt(p, mnt)) {
-               hlist_del_init_rcu(&p->mnt_hash);
-               hlist_add_head(&p->mnt_hash, &tmp_list);
+               p->mnt.mnt_flags |= MNT_UMOUNT;
+               list_move(&p->mnt_list, &tmp_list);
        }
 
-       hlist_for_each_entry(p, &tmp_list, mnt_hash)
+       /* Hide the mounts from mnt_mounts */
+       list_for_each_entry(p, &tmp_list, mnt_list) {
                list_del_init(&p->mnt_child);
+       }
 
-       if (how)
+       /* Add propogated mounts to the tmp_list */
+       if (how & UMOUNT_PROPAGATE)
                propagate_umount(&tmp_list);
 
-       while (!hlist_empty(&tmp_list)) {
-               p = hlist_entry(tmp_list.first, struct mount, mnt_hash);
-               hlist_del_init_rcu(&p->mnt_hash);
+       while (!list_empty(&tmp_list)) {
+               p = list_first_entry(&tmp_list, struct mount, mnt_list);
                list_del_init(&p->mnt_expire);
                list_del_init(&p->mnt_list);
                __touch_mnt_namespace(p->mnt_ns);
                p->mnt_ns = NULL;
-               if (how < 2)
+               if (how & UMOUNT_SYNC)
                        p->mnt.mnt_flags |= MNT_SYNC_UMOUNT;
 
                pin_insert_group(&p->mnt_umount, &p->mnt_parent->mnt, &unmounted);
@@ -1362,6 +1370,7 @@ void umount_tree(struct mount *mnt, int how)
                        p->mnt_mountpoint = p->mnt.mnt_root;
                        p->mnt_parent = p;
                        p->mnt_mp = NULL;
+                       hlist_del_init_rcu(&p->mnt_hash);
                }
                change_mnt_propagation(p, MS_PRIVATE);
        }
@@ -1447,14 +1456,14 @@ static int do_umount(struct mount *mnt, int flags)
 
        if (flags & MNT_DETACH) {
                if (!list_empty(&mnt->mnt_list))
-                       umount_tree(mnt, 2);
+                       umount_tree(mnt, UMOUNT_PROPAGATE);
                retval = 0;
        } else {
                shrink_submounts(mnt);
                retval = -EBUSY;
                if (!propagate_mount_busy(mnt, 2)) {
                        if (!list_empty(&mnt->mnt_list))
-                               umount_tree(mnt, 1);
+                               umount_tree(mnt, UMOUNT_PROPAGATE|UMOUNT_SYNC);
                        retval = 0;
                }
        }
@@ -1486,7 +1495,7 @@ void __detach_mounts(struct dentry *dentry)
        lock_mount_hash();
        while (!hlist_empty(&mp->m_list)) {
                mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
-               umount_tree(mnt, 2);
+               umount_tree(mnt, 0);
        }
        unlock_mount_hash();
        put_mountpoint(mp);
@@ -1648,7 +1657,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
 out:
        if (res) {
                lock_mount_hash();
-               umount_tree(res, 0);
+               umount_tree(res, UMOUNT_SYNC);
                unlock_mount_hash();
        }
        return q;
@@ -1672,7 +1681,7 @@ void drop_collected_mounts(struct vfsmount *mnt)
 {
        namespace_lock();
        lock_mount_hash();
-       umount_tree(real_mount(mnt), 0);
+       umount_tree(real_mount(mnt), UMOUNT_SYNC);
        unlock_mount_hash();
        namespace_unlock();
 }
@@ -1855,7 +1864,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
  out_cleanup_ids:
        while (!hlist_empty(&tree_list)) {
                child = hlist_entry(tree_list.first, struct mount, mnt_hash);
-               umount_tree(child, 0);
+               umount_tree(child, UMOUNT_SYNC);
        }
        unlock_mount_hash();
        cleanup_group_ids(source_mnt, NULL);
@@ -1907,8 +1916,8 @@ static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp)
        if (mnt->mnt.mnt_sb->s_flags & MS_NOUSER)
                return -EINVAL;
 
-       if (S_ISDIR(mp->m_dentry->d_inode->i_mode) !=
-             S_ISDIR(mnt->mnt.mnt_root->d_inode->i_mode))
+       if (d_is_dir(mp->m_dentry) !=
+             d_is_dir(mnt->mnt.mnt_root))
                return -ENOTDIR;
 
        return attach_recursive_mnt(mnt, p, mp, NULL);
@@ -2035,7 +2044,7 @@ static int do_loopback(struct path *path, const char *old_name,
        err = graft_tree(mnt, parent, mp);
        if (err) {
                lock_mount_hash();
-               umount_tree(mnt, 0);
+               umount_tree(mnt, UMOUNT_SYNC);
                unlock_mount_hash();
        }
 out2:
@@ -2180,8 +2189,8 @@ static int do_move_mount(struct path *path, const char *old_name)
        if (!mnt_has_parent(old))
                goto out1;
 
-       if (S_ISDIR(path->dentry->d_inode->i_mode) !=
-             S_ISDIR(old_path.dentry->d_inode->i_mode))
+       if (d_is_dir(path->dentry) !=
+             d_is_dir(old_path.dentry))
                goto out1;
        /*
         * Don't move a mount residing in a shared parent.
@@ -2271,7 +2280,7 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
                goto unlock;
 
        err = -EINVAL;
-       if (S_ISLNK(newmnt->mnt.mnt_root->d_inode->i_mode))
+       if (d_is_symlink(newmnt->mnt.mnt_root))
                goto unlock;
 
        newmnt->mnt.mnt_flags = mnt_flags;
@@ -2406,7 +2415,7 @@ void mark_mounts_for_expiry(struct list_head *mounts)
        while (!list_empty(&graveyard)) {
                mnt = list_first_entry(&graveyard, struct mount, mnt_expire);
                touch_mnt_namespace(mnt->mnt_ns);
-               umount_tree(mnt, 1);
+               umount_tree(mnt, UMOUNT_PROPAGATE|UMOUNT_SYNC);
        }
        unlock_mount_hash();
        namespace_unlock();
@@ -2477,7 +2486,7 @@ static void shrink_submounts(struct mount *mnt)
                        m = list_first_entry(&graveyard, struct mount,
                                                mnt_expire);
                        touch_mnt_namespace(m->mnt_ns);
-                       umount_tree(m, 1);
+                       umount_tree(m, UMOUNT_PROPAGATE|UMOUNT_SYNC);
                }
        }
 }