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