LCOV - code coverage report
Current view: top level - fs - proc_namespace.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 159 0.0 %
Date: 2023-07-19 18:55:55 Functions: 0 12 0.0 %

          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           0 : static __poll_t mounts_poll(struct file *file, poll_table *wait)
      22             : {
      23           0 :         struct seq_file *m = file->private_data;
      24           0 :         struct proc_mounts *p = m->private;
      25           0 :         struct mnt_namespace *ns = p->ns;
      26           0 :         __poll_t res = EPOLLIN | EPOLLRDNORM;
      27             :         int event;
      28             : 
      29           0 :         poll_wait(file, &p->ns->poll, wait);
      30             : 
      31           0 :         event = READ_ONCE(ns->event);
      32           0 :         if (m->poll_event != event) {
      33           0 :                 m->poll_event = event;
      34           0 :                 res |= EPOLLERR | EPOLLPRI;
      35             :         }
      36             : 
      37           0 :         return res;
      38             : }
      39             : 
      40             : struct proc_fs_opts {
      41             :         int flag;
      42             :         const char *str;
      43             : };
      44             : 
      45           0 : static int show_sb_opts(struct seq_file *m, struct super_block *sb)
      46             : {
      47             :         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             :         const struct proc_fs_opts *fs_infop;
      55             : 
      56           0 :         for (fs_infop = fs_opts; fs_infop->flag; fs_infop++) {
      57           0 :                 if (sb->s_flags & fs_infop->flag)
      58           0 :                         seq_puts(m, fs_infop->str);
      59             :         }
      60             : 
      61           0 :         return security_sb_show_options(m, sb);
      62             : }
      63             : 
      64           0 : static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
      65             : {
      66             :         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             :         const struct proc_fs_opts *fs_infop;
      77             : 
      78           0 :         for (fs_infop = mnt_opts; fs_infop->flag; fs_infop++) {
      79           0 :                 if (mnt->mnt_flags & fs_infop->flag)
      80           0 :                         seq_puts(m, fs_infop->str);
      81             :         }
      82             : 
      83           0 :         if (is_idmapped_mnt(mnt))
      84           0 :                 seq_puts(m, ",idmapped");
      85           0 : }
      86             : 
      87             : static inline void mangle(struct seq_file *m, const char *s)
      88             : {
      89           0 :         seq_escape(m, s, " \t\n\\#");
      90             : }
      91             : 
      92           0 : static void show_type(struct seq_file *m, struct super_block *sb)
      93             : {
      94           0 :         mangle(m, sb->s_type->name);
      95           0 :         if (sb->s_subtype) {
      96           0 :                 seq_putc(m, '.');
      97           0 :                 mangle(m, sb->s_subtype);
      98             :         }
      99           0 : }
     100             : 
     101           0 : static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
     102             : {
     103           0 :         struct proc_mounts *p = m->private;
     104           0 :         struct mount *r = real_mount(mnt);
     105           0 :         struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
     106           0 :         struct super_block *sb = mnt_path.dentry->d_sb;
     107             :         int err;
     108             : 
     109           0 :         if (sb->s_op->show_devname) {
     110           0 :                 err = sb->s_op->show_devname(m, mnt_path.dentry);
     111           0 :                 if (err)
     112             :                         goto out;
     113             :         } else {
     114           0 :                 mangle(m, r->mnt_devname ? r->mnt_devname : "none");
     115             :         }
     116           0 :         seq_putc(m, ' ');
     117             :         /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
     118           0 :         err = seq_path_root(m, &mnt_path, &p->root, " \t\n\\");
     119           0 :         if (err)
     120             :                 goto out;
     121           0 :         seq_putc(m, ' ');
     122           0 :         show_type(m, sb);
     123           0 :         seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw");
     124           0 :         err = show_sb_opts(m, sb);
     125           0 :         if (err)
     126             :                 goto out;
     127           0 :         show_mnt_opts(m, mnt);
     128           0 :         if (sb->s_op->show_options)
     129           0 :                 err = sb->s_op->show_options(m, mnt_path.dentry);
     130           0 :         seq_puts(m, " 0 0\n");
     131             : out:
     132           0 :         return err;
     133             : }
     134             : 
     135           0 : static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
     136             : {
     137           0 :         struct proc_mounts *p = m->private;
     138           0 :         struct mount *r = real_mount(mnt);
     139           0 :         struct super_block *sb = mnt->mnt_sb;
     140           0 :         struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
     141             :         int err;
     142             : 
     143           0 :         seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id,
     144           0 :                    MAJOR(sb->s_dev), MINOR(sb->s_dev));
     145           0 :         if (sb->s_op->show_path) {
     146           0 :                 err = sb->s_op->show_path(m, mnt->mnt_root);
     147           0 :                 if (err)
     148             :                         goto out;
     149             :         } else {
     150           0 :                 seq_dentry(m, mnt->mnt_root, " \t\n\\");
     151             :         }
     152           0 :         seq_putc(m, ' ');
     153             : 
     154             :         /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
     155           0 :         err = seq_path_root(m, &mnt_path, &p->root, " \t\n\\");
     156           0 :         if (err)
     157             :                 goto out;
     158             : 
     159           0 :         seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
     160           0 :         show_mnt_opts(m, mnt);
     161             : 
     162             :         /* Tagged fields ("foo:X" or "bar") */
     163           0 :         if (IS_MNT_SHARED(r))
     164           0 :                 seq_printf(m, " shared:%i", r->mnt_group_id);
     165           0 :         if (IS_MNT_SLAVE(r)) {
     166           0 :                 int master = r->mnt_master->mnt_group_id;
     167           0 :                 int dom = get_dominating_id(r, &p->root);
     168           0 :                 seq_printf(m, " master:%i", master);
     169           0 :                 if (dom && dom != master)
     170           0 :                         seq_printf(m, " propagate_from:%i", dom);
     171             :         }
     172           0 :         if (IS_MNT_UNBINDABLE(r))
     173           0 :                 seq_puts(m, " unbindable");
     174             : 
     175             :         /* Filesystem specific data */
     176           0 :         seq_puts(m, " - ");
     177           0 :         show_type(m, sb);
     178           0 :         seq_putc(m, ' ');
     179           0 :         if (sb->s_op->show_devname) {
     180           0 :                 err = sb->s_op->show_devname(m, mnt->mnt_root);
     181           0 :                 if (err)
     182             :                         goto out;
     183             :         } else {
     184           0 :                 mangle(m, r->mnt_devname ? r->mnt_devname : "none");
     185             :         }
     186           0 :         seq_puts(m, sb_rdonly(sb) ? " ro" : " rw");
     187           0 :         err = show_sb_opts(m, sb);
     188           0 :         if (err)
     189             :                 goto out;
     190           0 :         if (sb->s_op->show_options)
     191           0 :                 err = sb->s_op->show_options(m, mnt->mnt_root);
     192           0 :         seq_putc(m, '\n');
     193             : out:
     194           0 :         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             :         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             :                         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             :                 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             : out:
     239           0 :         return err;
     240             : }
     241             : 
     242           0 : static int mounts_open_common(struct inode *inode, struct file *file,
     243             :                               int (*show)(struct seq_file *, struct vfsmount *))
     244             : {
     245           0 :         struct task_struct *task = get_proc_task(inode);
     246             :         struct nsproxy *nsp;
     247           0 :         struct mnt_namespace *ns = NULL;
     248             :         struct path root;
     249             :         struct proc_mounts *p;
     250             :         struct seq_file *m;
     251           0 :         int ret = -EINVAL;
     252             : 
     253           0 :         if (!task)
     254             :                 goto err;
     255             : 
     256             :         task_lock(task);
     257           0 :         nsp = task->nsproxy;
     258           0 :         if (!nsp || !nsp->mnt_ns) {
     259             :                 task_unlock(task);
     260           0 :                 put_task_struct(task);
     261           0 :                 goto err;
     262             :         }
     263           0 :         ns = nsp->mnt_ns;
     264           0 :         get_mnt_ns(ns);
     265           0 :         if (!task->fs) {
     266             :                 task_unlock(task);
     267           0 :                 put_task_struct(task);
     268           0 :                 ret = -ENOENT;
     269           0 :                 goto err_put_ns;
     270             :         }
     271           0 :         get_fs_root(task->fs, &root);
     272             :         task_unlock(task);
     273           0 :         put_task_struct(task);
     274             : 
     275           0 :         ret = seq_open_private(file, &mounts_op, sizeof(struct proc_mounts));
     276           0 :         if (ret)
     277             :                 goto err_put_path;
     278             : 
     279           0 :         m = file->private_data;
     280           0 :         m->poll_event = ns->event;
     281             : 
     282           0 :         p = m->private;
     283           0 :         p->ns = ns;
     284           0 :         p->root = root;
     285           0 :         p->show = show;
     286           0 :         INIT_LIST_HEAD(&p->cursor.mnt_list);
     287           0 :         p->cursor.mnt.mnt_flags = MNT_CURSOR;
     288             : 
     289           0 :         return 0;
     290             : 
     291             :  err_put_path:
     292           0 :         path_put(&root);
     293             :  err_put_ns:
     294           0 :         put_mnt_ns(ns);
     295             :  err:
     296             :         return ret;
     297             : }
     298             : 
     299           0 : static int mounts_release(struct inode *inode, struct file *file)
     300             : {
     301           0 :         struct seq_file *m = file->private_data;
     302           0 :         struct proc_mounts *p = m->private;
     303           0 :         path_put(&p->root);
     304           0 :         mnt_cursor_del(p->ns, &p->cursor);
     305           0 :         put_mnt_ns(p->ns);
     306           0 :         return seq_release_private(inode, file);
     307             : }
     308             : 
     309           0 : static int mounts_open(struct inode *inode, struct file *file)
     310             : {
     311           0 :         return mounts_open_common(inode, file, show_vfsmnt);
     312             : }
     313             : 
     314           0 : static int mountinfo_open(struct inode *inode, struct file *file)
     315             : {
     316           0 :         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