mnt: Modify fs_fully_visible to deal with locked ro nodev and atime
[linux-2.6-block.git] / fs / namespace.c
index 8e7edaf60fe13edee5f180a87406b7c75dcefeb1..63b9806235e6471c4fb06af6ed1f0882e068c269 100644 (file)
@@ -2332,7 +2332,7 @@ unlock:
        return err;
 }
 
-static bool fs_fully_visible(struct file_system_type *fs_type);
+static bool fs_fully_visible(struct file_system_type *fs_type, int *new_mnt_flags);
 
 /*
  * create a new mount for userspace and request it to be added into the
@@ -2366,7 +2366,7 @@ static int do_new_mount(struct path *path, const char *fstype, int flags,
                        mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV;
                }
                if (type->fs_flags & FS_USERNS_VISIBLE) {
-                       if (!fs_fully_visible(type))
+                       if (!fs_fully_visible(type, &mnt_flags))
                                return -EPERM;
                }
        }
@@ -3170,9 +3170,10 @@ bool current_chrooted(void)
        return chrooted;
 }
 
-static bool fs_fully_visible(struct file_system_type *type)
+static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags)
 {
        struct mnt_namespace *ns = current->nsproxy->mnt_ns;
+       int new_flags = *new_mnt_flags;
        struct mount *mnt;
        bool visible = false;
 
@@ -3191,6 +3192,19 @@ static bool fs_fully_visible(struct file_system_type *type)
                if (mnt->mnt.mnt_root != mnt->mnt.mnt_sb->s_root)
                        continue;
 
+               /* Verify the mount flags are equal to or more permissive
+                * than the proposed new mount.
+                */
+               if ((mnt->mnt.mnt_flags & MNT_LOCK_READONLY) &&
+                   !(new_flags & MNT_READONLY))
+                       continue;
+               if ((mnt->mnt.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)))
+                       continue;
+
                /* This mount is not fully visible if there are any child mounts
                 * that cover anything except for empty directories.
                 */
@@ -3201,6 +3215,10 @@ static bool fs_fully_visible(struct file_system_type *type)
                        if (inode->i_nlink > 2)
                                goto next;
                }
+               /* Preserve the locked attributes */
+               *new_mnt_flags |= mnt->mnt.mnt_flags & (MNT_LOCK_READONLY | \
+                                                       MNT_LOCK_NODEV    | \
+                                                       MNT_LOCK_ATIME);
                visible = true;
                goto found;
        next:   ;