]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/namespace.c
Merge remote-tracking branch 'usb/usb-next'
[karo-tx-linux.git] / fs / namespace.c
index c7cb8a526c05fbaa5ba18934cd04eda1eb2a6e60..0570729c87fd22dd299c50ee0303f43c384e4932 100644 (file)
@@ -1361,6 +1361,36 @@ enum umount_tree_flags {
        UMOUNT_PROPAGATE = 2,
        UMOUNT_CONNECTED = 4,
 };
+
+static bool disconnect_mount(struct mount *mnt, enum umount_tree_flags how)
+{
+       /* Leaving mounts connected is only valid for lazy umounts */
+       if (how & UMOUNT_SYNC)
+               return true;
+
+       /* A mount without a parent has nothing to be connected to */
+       if (!mnt_has_parent(mnt))
+               return true;
+
+       /* Because the reference counting rules change when mounts are
+        * unmounted and connected, umounted mounts may not be
+        * connected to mounted mounts.
+        */
+       if (!(mnt->mnt_parent->mnt.mnt_flags & MNT_UMOUNT))
+               return true;
+
+       /* Has it been requested that the mount remain connected? */
+       if (how & UMOUNT_CONNECTED)
+               return false;
+
+       /* Is the mount locked such that it needs to remain connected? */
+       if (IS_MNT_LOCKED(mnt))
+               return false;
+
+       /* By default disconnect the mount */
+       return true;
+}
+
 /*
  * mount_lock must be held
  * namespace_sem must be held for write
@@ -1398,10 +1428,7 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
                if (how & UMOUNT_SYNC)
                        p->mnt.mnt_flags |= MNT_SYNC_UMOUNT;
 
-               disconnect = !(((how & UMOUNT_CONNECTED) &&
-                               mnt_has_parent(p) &&
-                               (p->mnt_parent->mnt.mnt_flags & MNT_UMOUNT)) ||
-                              IS_MNT_LOCKED_AND_LAZY(p));
+               disconnect = disconnect_mount(p, how);
 
                pin_insert_group(&p->mnt_umount, &p->mnt_parent->mnt,
                                 disconnect ? &unmounted : NULL);
@@ -1538,11 +1565,8 @@ void __detach_mounts(struct dentry *dentry)
        while (!hlist_empty(&mp->m_list)) {
                mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
                if (mnt->mnt.mnt_flags & MNT_UMOUNT) {
-                       struct mount *p, *tmp;
-                       list_for_each_entry_safe(p, tmp, &mnt->mnt_mounts,  mnt_child) {
-                               hlist_add_head(&p->mnt_umount.s_list, &unmounted);
-                               umount_mnt(p);
-                       }
+                       hlist_add_head(&mnt->mnt_umount.s_list, &unmounted);
+                       umount_mnt(mnt);
                }
                else umount_tree(mnt, UMOUNT_CONNECTED);
        }
@@ -3194,6 +3218,8 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
        down_read(&namespace_sem);
        list_for_each_entry(mnt, &ns->list, mnt_list) {
                struct mount *child;
+               int mnt_flags;
+
                if (mnt->mnt.mnt_sb->s_type != type)
                        continue;
 
@@ -3203,17 +3229,30 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
                if (mnt->mnt.mnt_root != mnt->mnt.mnt_sb->s_root)
                        continue;
 
+               /* Read the mount flags and filter out flags that
+                * may safely be ignored.
+                */
+               mnt_flags = mnt->mnt.mnt_flags;
+               if (mnt->mnt.mnt_sb->s_iflags & SB_I_NOEXEC)
+                       mnt_flags &= ~(MNT_LOCK_NOSUID | MNT_LOCK_NOEXEC);
+
                /* Verify the mount flags are equal to or more permissive
                 * than the proposed new mount.
                 */
-               if ((mnt->mnt.mnt_flags & MNT_LOCK_READONLY) &&
+               if ((mnt_flags & MNT_LOCK_READONLY) &&
                    !(new_flags & MNT_READONLY))
                        continue;
-               if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) &&
+               if ((mnt_flags & MNT_LOCK_NODEV) &&
                    !(new_flags & MNT_NODEV))
                        continue;
-               if ((mnt->mnt.mnt_flags & MNT_LOCK_ATIME) &&
-                   ((mnt->mnt.mnt_flags & MNT_ATIME_MASK) != (new_flags & MNT_ATIME_MASK)))
+               if ((mnt_flags & MNT_LOCK_NOSUID) &&
+                   !(new_flags & MNT_NOSUID))
+                       continue;
+               if ((mnt_flags & MNT_LOCK_NOEXEC) &&
+                   !(new_flags & MNT_NOEXEC))
+                       continue;
+               if ((mnt_flags & MNT_LOCK_ATIME) &&
+                   ((mnt_flags & MNT_ATIME_MASK) != (new_flags & MNT_ATIME_MASK)))
                        continue;
 
                /* This mount is not fully visible if there are any
@@ -3223,16 +3262,18 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
                list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
                        struct inode *inode = child->mnt_mountpoint->d_inode;
                        /* Only worry about locked mounts */
-                       if (!(mnt->mnt.mnt_flags & MNT_LOCKED))
+                       if (!(mnt_flags & MNT_LOCKED))
                                continue;
                        /* Is the directory permanetly empty? */
                        if (!is_empty_dir_inode(inode))
                                goto next;
                }
                /* Preserve the locked attributes */
-               *new_mnt_flags |= mnt->mnt.mnt_flags & (MNT_LOCK_READONLY | \
-                                                       MNT_LOCK_NODEV    | \
-                                                       MNT_LOCK_ATIME);
+               *new_mnt_flags |= mnt_flags & (MNT_LOCK_READONLY | \
+                                              MNT_LOCK_NODEV    | \
+                                              MNT_LOCK_NOSUID   | \
+                                              MNT_LOCK_NOEXEC   | \
+                                              MNT_LOCK_ATIME);
                visible = true;
                goto found;
        next:   ;