LCOV - code coverage report
Current view: top level - fs - proc_namespace.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 135 193 69.9 %
Date: 2021-04-22 12:43:58 Functions: 10 12 83.3 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * fs/proc_namespace.c - handling of /proc/<pid>/{mounts,mountinfo,mountstats}
       4             :  *
       5             :  * In fact, that's a piece of procfs; it's *almost* isolated from
       6             :  * the rest of fs/proc, but has rather close relationships with
       7             :  * fs/namespace.c, thus here instead of fs/proc
       8             :  *
       9             :  */
      10             : #include <linux/mnt_namespace.h>
      11             : #include <linux/nsproxy.h>
      12             : #include <linux/security.h>
      13             : #include <linux/fs_struct.h>
      14             : #include <linux/sched/task.h>
      15             : 
      16             : #include "proc/internal.h" /* only for get_proc_task() in ->open() */
      17             : 
      18             : #include "pnode.h"
      19             : #include "internal.h"
      20             : 
      21         248 : static __poll_t mounts_poll(struct file *file, poll_table *wait)
      22             : {
      23         248 :         struct seq_file *m = file->private_data;
      24         248 :         struct proc_mounts *p = m->private;
      25         248 :         struct mnt_namespace *ns = p->ns;
      26         248 :         __poll_t res = EPOLLIN | EPOLLRDNORM;
      27         248 :         int event;
      28             : 
      29         248 :         poll_wait(file, &p->ns->poll, wait);
      30             : 
      31         251 :         event = READ_ONCE(ns->event);
      32         251 :         if (m->poll_event != event) {
      33         124 :                 m->poll_event = event;
      34         124 :                 res |= EPOLLERR | EPOLLPRI;
      35             :         }
      36             : 
      37         251 :         return res;
      38             : }
      39             : 
      40             : struct proc_fs_opts {
      41             :         int flag;
      42             :         const char *str;
      43             : };
      44             : 
      45        4838 : static int show_sb_opts(struct seq_file *m, struct super_block *sb)
      46             : {
      47        4838 :         static const struct proc_fs_opts fs_opts[] = {
      48             :                 { SB_SYNCHRONOUS, ",sync" },
      49             :                 { SB_DIRSYNC, ",dirsync" },
      50             :                 { SB_MANDLOCK, ",mand" },
      51             :                 { SB_LAZYTIME, ",lazytime" },
      52             :                 { 0, NULL }
      53             :         };
      54        4838 :         const struct proc_fs_opts *fs_infop;
      55             : 
      56       24198 :         for (fs_infop = fs_opts; fs_infop->flag; fs_infop++) {
      57       19357 :                 if (sb->s_flags & fs_infop->flag)
      58           0 :                         seq_puts(m, fs_infop->str);
      59             :         }
      60             : 
      61        4841 :         return security_sb_show_options(m, sb);
      62             : }
      63             : 
      64        4839 : static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
      65             : {
      66        4839 :         static const struct proc_fs_opts mnt_opts[] = {
      67             :                 { MNT_NOSUID, ",nosuid" },
      68             :                 { MNT_NODEV, ",nodev" },
      69             :                 { MNT_NOEXEC, ",noexec" },
      70             :                 { MNT_NOATIME, ",noatime" },
      71             :                 { MNT_NODIRATIME, ",nodiratime" },
      72             :                 { MNT_RELATIME, ",relatime" },
      73             :                 { MNT_NOSYMFOLLOW, ",nosymfollow" },
      74             :                 { 0, NULL }
      75             :         };
      76        4839 :         const struct proc_fs_opts *fs_infop;
      77             : 
      78       38718 :         for (fs_infop = mnt_opts; fs_infop->flag; fs_infop++) {
      79       33877 :                 if (mnt->mnt_flags & fs_infop->flag)
      80       12392 :                         seq_puts(m, fs_infop->str);
      81             :         }
      82             : 
      83        4841 :         if (mnt_user_ns(mnt) != &init_user_ns)
      84           0 :                 seq_puts(m, ",idmapped");
      85        4841 : }
      86             : 
      87        9678 : static inline void mangle(struct seq_file *m, const char *s)
      88             : {
      89        4838 :         seq_escape(m, s, " \t\n\\");
      90        4843 : }
      91             : 
      92        4840 : static void show_type(struct seq_file *m, struct super_block *sb)
      93             : {
      94        4840 :         mangle(m, sb->s_type->name);
      95        4835 :         if (sb->s_subtype) {
      96           0 :                 seq_putc(m, '.');
      97           0 :                 mangle(m, sb->s_subtype);
      98             :         }
      99        4835 : }
     100             : 
     101          28 : static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
     102             : {
     103          28 :         struct proc_mounts *p = m->private;
     104          28 :         struct mount *r = real_mount(mnt);
     105          28 :         struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
     106          28 :         struct super_block *sb = mnt_path.dentry->d_sb;
     107          28 :         int err;
     108             : 
     109          28 :         if (sb->s_op->show_devname) {
     110           0 :                 err = sb->s_op->show_devname(m, mnt_path.dentry);
     111           0 :                 if (err)
     112           0 :                         goto out;
     113             :         } else {
     114          28 :                 mangle(m, r->mnt_devname ? r->mnt_devname : "none");
     115             :         }
     116          28 :         seq_putc(m, ' ');
     117             :         /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
     118          28 :         err = seq_path_root(m, &mnt_path, &p->root, " \t\n\\");
     119          28 :         if (err)
     120           2 :                 goto out;
     121          26 :         seq_putc(m, ' ');
     122          26 :         show_type(m, sb);
     123          49 :         seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw");
     124          26 :         err = show_sb_opts(m, sb);
     125          26 :         if (err)
     126           0 :                 goto out;
     127          26 :         show_mnt_opts(m, mnt);
     128          26 :         if (sb->s_op->show_options)
     129          24 :                 err = sb->s_op->show_options(m, mnt_path.dentry);
     130          26 :         seq_puts(m, " 0 0\n");
     131          28 : out:
     132          28 :         return err;
     133             : }
     134             : 
     135        5022 : static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
     136             : {
     137        5022 :         struct proc_mounts *p = m->private;
     138        5022 :         struct mount *r = real_mount(mnt);
     139        5022 :         struct super_block *sb = mnt->mnt_sb;
     140        5022 :         struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
     141        5022 :         int err;
     142             : 
     143        5022 :         seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id,
     144        5022 :                    MAJOR(sb->s_dev), MINOR(sb->s_dev));
     145        5021 :         if (sb->s_op->show_path) {
     146         903 :                 err = sb->s_op->show_path(m, mnt->mnt_root);
     147         902 :                 if (err)
     148           0 :                         goto out;
     149             :         } else {
     150        4118 :                 seq_dentry(m, mnt->mnt_root, " \t\n\\");
     151             :         }
     152        5020 :         seq_putc(m, ' ');
     153             : 
     154             :         /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
     155        5021 :         err = seq_path_root(m, &mnt_path, &p->root, " \t\n\\");
     156        5021 :         if (err)
     157         206 :                 goto out;
     158             : 
     159        8777 :         seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
     160        4815 :         show_mnt_opts(m, mnt);
     161             : 
     162             :         /* Tagged fields ("foo:X" or "bar") */
     163        4815 :         if (IS_MNT_SHARED(r))
     164        1850 :                 seq_printf(m, " shared:%i", r->mnt_group_id);
     165        4815 :         if (IS_MNT_SLAVE(r)) {
     166        2893 :                 int master = r->mnt_master->mnt_group_id;
     167        2893 :                 int dom = get_dominating_id(r, &p->root);
     168        2893 :                 seq_printf(m, " master:%i", master);
     169        2893 :                 if (dom && dom != master)
     170           0 :                         seq_printf(m, " propagate_from:%i", dom);
     171             :         }
     172        4815 :         if (IS_MNT_UNBINDABLE(r))
     173           0 :                 seq_puts(m, " unbindable");
     174             : 
     175             :         /* Filesystem specific data */
     176        4815 :         seq_puts(m, " - ");
     177        4814 :         show_type(m, sb);
     178        4809 :         seq_putc(m, ' ');
     179        4810 :         if (sb->s_op->show_devname) {
     180           0 :                 err = sb->s_op->show_devname(m, mnt->mnt_root);
     181           0 :                 if (err)
     182           0 :                         goto out;
     183             :         } else {
     184        4810 :                 mangle(m, r->mnt_devname ? r->mnt_devname : "none");
     185             :         }
     186        9340 :         seq_puts(m, sb_rdonly(sb) ? " ro" : " rw");
     187        4814 :         err = show_sb_opts(m, sb);
     188        4815 :         if (err)
     189           0 :                 goto out;
     190        4815 :         if (sb->s_op->show_options)
     191        4532 :                 err = sb->s_op->show_options(m, mnt->mnt_root);
     192        4814 :         seq_putc(m, '\n');
     193        5020 : out:
     194        5020 :         return err;
     195             : }
     196             : 
     197           0 : static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt)
     198             : {
     199           0 :         struct proc_mounts *p = m->private;
     200           0 :         struct mount *r = real_mount(mnt);
     201           0 :         struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
     202           0 :         struct super_block *sb = mnt_path.dentry->d_sb;
     203           0 :         int err;
     204             : 
     205             :         /* device */
     206           0 :         if (sb->s_op->show_devname) {
     207           0 :                 seq_puts(m, "device ");
     208           0 :                 err = sb->s_op->show_devname(m, mnt_path.dentry);
     209           0 :                 if (err)
     210           0 :                         goto out;
     211             :         } else {
     212           0 :                 if (r->mnt_devname) {
     213           0 :                         seq_puts(m, "device ");
     214           0 :                         mangle(m, r->mnt_devname);
     215             :                 } else
     216           0 :                         seq_puts(m, "no device");
     217             :         }
     218             : 
     219             :         /* mount point */
     220           0 :         seq_puts(m, " mounted on ");
     221             :         /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
     222           0 :         err = seq_path_root(m, &mnt_path, &p->root, " \t\n\\");
     223           0 :         if (err)
     224           0 :                 goto out;
     225           0 :         seq_putc(m, ' ');
     226             : 
     227             :         /* file system type */
     228           0 :         seq_puts(m, "with fstype ");
     229           0 :         show_type(m, sb);
     230             : 
     231             :         /* optional statistics */
     232           0 :         if (sb->s_op->show_stats) {
     233           0 :                 seq_putc(m, ' ');
     234           0 :                 err = sb->s_op->show_stats(m, mnt_path.dentry);
     235             :         }
     236             : 
     237           0 :         seq_putc(m, '\n');
     238           0 : out:
     239           0 :         return err;
     240             : }
     241             : 
     242         150 : static int mounts_open_common(struct inode *inode, struct file *file,
     243             :                               int (*show)(struct seq_file *, struct vfsmount *))
     244             : {
     245         150 :         struct task_struct *task = get_proc_task(inode);
     246         152 :         struct nsproxy *nsp;
     247         152 :         struct mnt_namespace *ns = NULL;
     248         152 :         struct path root;
     249         152 :         struct proc_mounts *p;
     250         152 :         struct seq_file *m;
     251         152 :         int ret = -EINVAL;
     252             : 
     253         152 :         if (!task)
     254           0 :                 goto err;
     255             : 
     256         152 :         task_lock(task);
     257         150 :         nsp = task->nsproxy;
     258         150 :         if (!nsp || !nsp->mnt_ns) {
     259           0 :                 task_unlock(task);
     260           0 :                 put_task_struct(task);
     261           0 :                 goto err;
     262             :         }
     263         150 :         ns = nsp->mnt_ns;
     264         150 :         get_mnt_ns(ns);
     265         151 :         if (!task->fs) {
     266           0 :                 task_unlock(task);
     267           0 :                 put_task_struct(task);
     268           0 :                 ret = -ENOENT;
     269           0 :                 goto err_put_ns;
     270             :         }
     271         151 :         get_fs_root(task->fs, &root);
     272         152 :         task_unlock(task);
     273         152 :         put_task_struct(task);
     274             : 
     275         152 :         ret = seq_open_private(file, &mounts_op, sizeof(struct proc_mounts));
     276         152 :         if (ret)
     277           0 :                 goto err_put_path;
     278             : 
     279         152 :         m = file->private_data;
     280         152 :         m->poll_event = ns->event;
     281             : 
     282         152 :         p = m->private;
     283         152 :         p->ns = ns;
     284         152 :         p->root = root;
     285         152 :         p->show = show;
     286         152 :         INIT_LIST_HEAD(&p->cursor.mnt_list);
     287         152 :         p->cursor.mnt.mnt_flags = MNT_CURSOR;
     288             : 
     289         152 :         return 0;
     290             : 
     291           0 :  err_put_path:
     292           0 :         path_put(&root);
     293           0 :  err_put_ns:
     294           0 :         put_mnt_ns(ns);
     295             :  err:
     296             :         return ret;
     297             : }
     298             : 
     299         150 : static int mounts_release(struct inode *inode, struct file *file)
     300             : {
     301         150 :         struct seq_file *m = file->private_data;
     302         150 :         struct proc_mounts *p = m->private;
     303         150 :         path_put(&p->root);
     304         150 :         mnt_cursor_del(p->ns, &p->cursor);
     305         150 :         put_mnt_ns(p->ns);
     306         150 :         return seq_release_private(inode, file);
     307             : }
     308             : 
     309           2 : static int mounts_open(struct inode *inode, struct file *file)
     310             : {
     311           2 :         return mounts_open_common(inode, file, show_vfsmnt);
     312             : }
     313             : 
     314         148 : static int mountinfo_open(struct inode *inode, struct file *file)
     315             : {
     316         148 :         return mounts_open_common(inode, file, show_mountinfo);
     317             : }
     318             : 
     319           0 : static int mountstats_open(struct inode *inode, struct file *file)
     320             : {
     321           0 :         return mounts_open_common(inode, file, show_vfsstat);
     322             : }
     323             : 
     324             : const struct file_operations proc_mounts_operations = {
     325             :         .open           = mounts_open,
     326             :         .read_iter      = seq_read_iter,
     327             :         .splice_read    = generic_file_splice_read,
     328             :         .llseek         = seq_lseek,
     329             :         .release        = mounts_release,
     330             :         .poll           = mounts_poll,
     331             : };
     332             : 
     333             : const struct file_operations proc_mountinfo_operations = {
     334             :         .open           = mountinfo_open,
     335             :         .read_iter      = seq_read_iter,
     336             :         .splice_read    = generic_file_splice_read,
     337             :         .llseek         = seq_lseek,
     338             :         .release        = mounts_release,
     339             :         .poll           = mounts_poll,
     340             : };
     341             : 
     342             : const struct file_operations proc_mountstats_operations = {
     343             :         .open           = mountstats_open,
     344             :         .read_iter      = seq_read_iter,
     345             :         .splice_read    = generic_file_splice_read,
     346             :         .llseek         = seq_lseek,
     347             :         .release        = mounts_release,
     348             : };

Generated by: LCOV version 1.14