Merge branch 'from-miklos' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 Jun 2020 23:44:06 +0000 (16:44 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 Jun 2020 23:44:06 +0000 (16:44 -0700)
Pull vfs updates from Al Viro:
 "Assorted patches from Miklos.

  An interesting part here is /proc/mounts stuff..."

The "/proc/mounts stuff" is using a cursor for keeeping the location
data while traversing the mount listing.

Also probably worth noting is the addition of faccessat2(), which takes
an additional set of flags to specify how the lookup is done
(AT_EACCESS, AT_SYMLINK_NOFOLLOW, AT_EMPTY_PATH).

* 'from-miklos' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  vfs: add faccessat2 syscall
  vfs: don't parse "silent" option
  vfs: don't parse "posixacl" option
  vfs: don't parse forbidden flags
  statx: add mount_root
  statx: add mount ID
  statx: don't clear STATX_ATIME on SB_RDONLY
  uapi: deprecate STATX_ALL
  utimensat: AT_EMPTY_PATH support
  vfs: split out access_override_creds()
  proc/mounts: add cursor
  aio: fix async fsync creds
  vfs: allow unprivileged whiteout creation

1  2 
fs/namespace.c
include/linux/device_cgroup.h
include/linux/fs.h

diff --combined fs/namespace.c
@@@ -648,6 -648,21 +648,21 @@@ struct vfsmount *lookup_mnt(const struc
        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.
@@@ -673,11 -688,15 +688,15 @@@ bool __is_local_mountpoint(struct dentr
                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 -1264,71 +1264,71 @@@ struct vfsmount *mnt_clone_internal(con
  }
  
  #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 -1338,15 +1338,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 */
  
  /**
@@@ -3202,6 -3255,7 +3255,7 @@@ static struct mnt_namespace *alloc_mnt_
        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;
@@@ -3595,7 -3649,7 +3649,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:
@@@ -3842,10 -3896,14 +3896,14 @@@ static bool mnt_already_visible(struct 
        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;
  
        next:   ;
        }
  found:
+       unlock_ns_list(ns);
        up_read(&namespace_sem);
        return visible;
  }
@@@ -1,5 -1,6 +1,5 @@@
  /* SPDX-License-Identifier: GPL-2.0 */
  #include <linux/fs.h>
 -#include <linux/bpf-cgroup.h>
  
  #define DEVCG_ACC_MKNOD 1
  #define DEVCG_ACC_READ  2
  #define DEVCG_DEV_CHAR  2
  #define DEVCG_DEV_ALL   4  /* this represents all devices */
  
 -#ifdef CONFIG_CGROUP_DEVICE
 -int devcgroup_check_permission(short type, u32 major, u32 minor,
 -                             short access);
 -#else
 -static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
 -                                           short access)
 -{ return 0; }
 -#endif
  
  #if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
 +int devcgroup_check_permission(short type, u32 major, u32 minor,
 +                             short access);
  static inline int devcgroup_inode_permission(struct inode *inode, int mask)
  {
        short type, access = 0;
@@@ -44,6 -51,9 +44,9 @@@ static inline int devcgroup_inode_mknod
        if (!S_ISBLK(mode) && !S_ISCHR(mode))
                return 0;
  
+       if (S_ISCHR(mode) && dev == WHITEOUT_DEV)
+               return 0;
        if (S_ISBLK(mode))
                type = DEVCG_DEV_BLOCK;
        else
@@@ -54,9 -64,6 +57,9 @@@
  }
  
  #else
 +static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
 +                             short access)
 +{ return 0; }
  static inline int devcgroup_inode_permission(struct inode *inode, int mask)
  { return 0; }
  static inline int devcgroup_inode_mknod(int mode, dev_t dev)
diff --combined include/linux/fs.h
@@@ -983,7 -983,7 +983,7 @@@ struct file_handle 
        __u32 handle_bytes;
        int handle_type;
        /* file identifier */
 -      unsigned char f_handle[0];
 +      unsigned char f_handle[];
  };
  
  static inline struct file *get_file(struct file *f)
@@@ -1721,7 -1721,11 +1721,11 @@@ extern int vfs_link(struct dentry *, st
  extern int vfs_rmdir(struct inode *, struct dentry *);
  extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
  extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
- extern int vfs_whiteout(struct inode *, struct dentry *);
+ static inline int vfs_whiteout(struct inode *dir, struct dentry *dentry)
+ {
+       return vfs_mknod(dir, dentry, S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
+ }
  
  extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode,
                                  int open_flag);