]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 7 Sep 2013 21:36:57 +0000 (14:36 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 7 Sep 2013 21:36:57 +0000 (14:36 -0700)
Pull vfs pile 2 (of many) from Al Viro:
 "Mostly Miklos' series this time"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  constify dcache.c inlined helpers where possible
  fuse: drop dentry on failed revalidate
  fuse: clean up return in fuse_dentry_revalidate()
  fuse: use d_materialise_unique()
  sysfs: use check_submounts_and_drop()
  nfs: use check_submounts_and_drop()
  gfs2: use check_submounts_and_drop()
  afs: use check_submounts_and_drop()
  vfs: check unlinked ancestors before mount
  vfs: check submounts and drop atomically
  vfs: add d_walk()
  vfs: restructure d_genocide()

1  2 
fs/namespace.c

diff --combined fs/namespace.c
index ef69fa5d2e5bd4806eda5d82f3c30e47ebf19c35,5997887cc64adbb51e244186bec96b257410c54c..fc2b5226278dbc4d8c0daaa1a2be987fbc7eca5e
@@@ -611,6 -611,7 +611,7 @@@ static struct mountpoint *new_mountpoin
  {
        struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry);
        struct mountpoint *mp;
+       int ret;
  
        list_for_each_entry(mp, chain, m_hash) {
                if (mp->m_dentry == dentry) {
        if (!mp)
                return ERR_PTR(-ENOMEM);
  
-       spin_lock(&dentry->d_lock);
-       if (d_unlinked(dentry)) {
-               spin_unlock(&dentry->d_lock);
+       ret = d_set_mounted(dentry);
+       if (ret) {
                kfree(mp);
-               return ERR_PTR(-ENOENT);
+               return ERR_PTR(ret);
        }
-       dentry->d_flags |= DCACHE_MOUNTED;
-       spin_unlock(&dentry->d_lock);
        mp->m_dentry = dentry;
        mp->m_count = 1;
        list_add(&mp->m_hash, chain);
@@@ -831,10 -830,6 +830,10 @@@ static struct mount *clone_mnt(struct m
        if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
                mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
  
 +      /* Don't allow unprivileged users to reveal what is under a mount */
 +      if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire))
 +              mnt->mnt.mnt_flags |= MNT_LOCKED;
 +
        atomic_inc(&sb->s_active);
        mnt->mnt.mnt_sb = sb;
        mnt->mnt.mnt_root = dget(root);
@@@ -1331,8 -1326,6 +1330,8 @@@ SYSCALL_DEFINE2(umount, char __user *, 
                goto dput_and_out;
        if (!check_mnt(mnt))
                goto dput_and_out;
 +      if (mnt->mnt.mnt_flags & MNT_LOCKED)
 +              goto dput_and_out;
  
        retval = do_umount(mnt, flags);
  dput_and_out:
@@@ -1355,11 -1348,14 +1354,11 @@@ SYSCALL_DEFINE1(oldumount, char __user 
  
  #endif
  
 -static bool mnt_ns_loop(struct path *path)
 +static bool is_mnt_ns_file(struct dentry *dentry)
  {
 -      /* Could bind mounting the mount namespace inode cause a
 -       * mount namespace loop?
 -       */
 -      struct inode *inode = path->dentry->d_inode;
 +      /* Is this a proxy for a mount namespace? */
 +      struct inode *inode = dentry->d_inode;
        struct proc_ns *ei;
 -      struct mnt_namespace *mnt_ns;
  
        if (!proc_ns_inode(inode))
                return false;
        if (ei->ns_ops != &mntns_operations)
                return false;
  
 -      mnt_ns = ei->ns;
 +      return true;
 +}
 +
 +static bool mnt_ns_loop(struct dentry *dentry)
 +{
 +      /* Could bind mounting the mount namespace inode cause a
 +       * mount namespace loop?
 +       */
 +      struct mnt_namespace *mnt_ns;
 +      if (!is_mnt_ns_file(dentry))
 +              return false;
 +
 +      mnt_ns = get_proc_ns(dentry->d_inode)->ns;
        return current->nsproxy->mnt_ns->seq >= mnt_ns->seq;
  }
  
@@@ -1389,17 -1373,13 +1388,17 @@@ struct mount *copy_tree(struct mount *m
  {
        struct mount *res, *p, *q, *r, *parent;
  
 -      if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
 +      if (!(flag & CL_COPY_UNBINDABLE) && IS_MNT_UNBINDABLE(mnt))
 +              return ERR_PTR(-EINVAL);
 +
 +      if (!(flag & CL_COPY_MNT_NS_FILE) && is_mnt_ns_file(dentry))
                return ERR_PTR(-EINVAL);
  
        res = q = clone_mnt(mnt, dentry, flag);
        if (IS_ERR(q))
                return q;
  
 +      q->mnt.mnt_flags &= ~MNT_LOCKED;
        q->mnt_mountpoint = mnt->mnt_mountpoint;
  
        p = mnt;
                        continue;
  
                for (s = r; s; s = next_mnt(s, r)) {
 -                      if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) {
 +                      if (!(flag & CL_COPY_UNBINDABLE) &&
 +                          IS_MNT_UNBINDABLE(s)) {
 +                              s = skip_mnt_tree(s);
 +                              continue;
 +                      }
 +                      if (!(flag & CL_COPY_MNT_NS_FILE) &&
 +                          is_mnt_ns_file(s->mnt.mnt_root)) {
                                s = skip_mnt_tree(s);
                                continue;
                        }
@@@ -1721,19 -1695,6 +1720,19 @@@ static int do_change_type(struct path *
        return err;
  }
  
 +static bool has_locked_children(struct mount *mnt, struct dentry *dentry)
 +{
 +      struct mount *child;
 +      list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
 +              if (!is_subdir(child->mnt_mountpoint, dentry))
 +                      continue;
 +
 +              if (child->mnt.mnt_flags & MNT_LOCKED)
 +                      return true;
 +      }
 +      return false;
 +}
 +
  /*
   * do loopback mount.
   */
@@@ -1751,7 -1712,7 +1750,7 @@@ static int do_loopback(struct path *pat
                return err;
  
        err = -EINVAL;
 -      if (mnt_ns_loop(&old_path))
 +      if (mnt_ns_loop(old_path.dentry))
                goto out; 
  
        mp = lock_mount(path);
        if (!check_mnt(parent) || !check_mnt(old))
                goto out2;
  
 +      if (!recurse && has_locked_children(old, old_path.dentry))
 +              goto out2;
 +
        if (recurse)
 -              mnt = copy_tree(old, old_path.dentry, 0);
 +              mnt = copy_tree(old, old_path.dentry, CL_COPY_MNT_NS_FILE);
        else
                mnt = clone_mnt(old, old_path.dentry, 0);
  
                goto out2;
        }
  
 +      mnt->mnt.mnt_flags &= ~MNT_LOCKED;
 +
        err = graft_tree(mnt, parent, mp);
        if (err) {
                br_write_lock(&vfsmount_lock);
@@@ -1896,9 -1852,6 +1895,9 @@@ static int do_move_mount(struct path *p
        if (!check_mnt(p) || !check_mnt(old))
                goto out1;
  
 +      if (old->mnt.mnt_flags & MNT_LOCKED)
 +              goto out1;
 +
        err = -EINVAL;
        if (old_path.dentry != old_path.mnt->mnt_root)
                goto out1;
@@@ -2435,7 -2388,7 +2434,7 @@@ static struct mnt_namespace *dup_mnt_ns
  
        namespace_lock();
        /* First pass: copy the tree topology */
 -      copy_flags = CL_COPY_ALL | CL_EXPIRE;
 +      copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE;
        if (user_ns != mnt_ns->user_ns)
                copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;
        new = copy_tree(old, old->mnt.mnt_root, copy_flags);
                }
                p = next_mnt(p, old);
                q = next_mnt(q, new);
 +              if (!q)
 +                      break;
 +              while (p->mnt.mnt_root != q->mnt.mnt_root)
 +                      p = next_mnt(p, old);
        }
        namespace_unlock();
  
@@@ -2680,8 -2629,6 +2679,8 @@@ SYSCALL_DEFINE2(pivot_root, const char 
                goto out4;
        if (!check_mnt(root_mnt) || !check_mnt(new_mnt))
                goto out4;
 +      if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
 +              goto out4;
        error = -ENOENT;
        if (d_unlinked(new.dentry))
                goto out4;
        br_write_lock(&vfsmount_lock);
        detach_mnt(new_mnt, &parent_path);
        detach_mnt(root_mnt, &root_parent);
 +      if (root_mnt->mnt.mnt_flags & MNT_LOCKED) {
 +              new_mnt->mnt.mnt_flags |= MNT_LOCKED;
 +              root_mnt->mnt.mnt_flags &= ~MNT_LOCKED;
 +      }
        /* mount old root on put_old */
        attach_mnt(root_mnt, old_mnt, old_mp);
        /* mount new_root on / */
@@@ -2867,38 -2810,25 +2866,38 @@@ bool current_chrooted(void
        return chrooted;
  }
  
 -void update_mnt_policy(struct user_namespace *userns)
 +bool fs_fully_visible(struct file_system_type *type)
  {
        struct mnt_namespace *ns = current->nsproxy->mnt_ns;
        struct mount *mnt;
 +      bool visible = false;
  
 -      down_read(&namespace_sem);
 +      if (unlikely(!ns))
 +              return false;
 +
 +      namespace_lock();
        list_for_each_entry(mnt, &ns->list, mnt_list) {
 -              switch (mnt->mnt.mnt_sb->s_magic) {
 -              case SYSFS_MAGIC:
 -                      userns->may_mount_sysfs = true;
 -                      break;
 -              case PROC_SUPER_MAGIC:
 -                      userns->may_mount_proc = true;
 -                      break;
 +              struct mount *child;
 +              if (mnt->mnt.mnt_sb->s_type != type)
 +                      continue;
 +
 +              /* This mount is not fully visible if there are any child mounts
 +               * that cover anything except for empty directories.
 +               */
 +              list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
 +                      struct inode *inode = child->mnt_mountpoint->d_inode;
 +                      if (!S_ISDIR(inode->i_mode))
 +                              goto next;
 +                      if (inode->i_nlink != 2)
 +                              goto next;
                }
 -              if (userns->may_mount_sysfs && userns->may_mount_proc)
 -                      break;
 +              visible = true;
 +              goto found;
 +      next:   ;
        }
 -      up_read(&namespace_sem);
 +found:
 +      namespace_unlock();
 +      return visible;
  }
  
  static void *mntns_get(struct task_struct *task)
@@@ -2929,8 -2859,8 +2928,8 @@@ static int mntns_install(struct nsprox
        struct path root;
  
        if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) ||
 -          !nsown_capable(CAP_SYS_CHROOT) ||
 -          !nsown_capable(CAP_SYS_ADMIN))
 +          !ns_capable(current_user_ns(), CAP_SYS_CHROOT) ||
 +          !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
                return -EPERM;
  
        if (fs->users != 1)