1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
20226f492SAl Viro /*
30226f492SAl Viro * fs/proc_namespace.c - handling of /proc/<pid>/{mounts,mountinfo,mountstats}
40226f492SAl Viro *
50226f492SAl Viro * In fact, that's a piece of procfs; it's *almost* isolated from
60226f492SAl Viro * the rest of fs/proc, but has rather close relationships with
70226f492SAl Viro * fs/namespace.c, thus here instead of fs/proc
80226f492SAl Viro *
90226f492SAl Viro */
100226f492SAl Viro #include <linux/mnt_namespace.h>
110226f492SAl Viro #include <linux/nsproxy.h>
120226f492SAl Viro #include <linux/security.h>
130226f492SAl Viro #include <linux/fs_struct.h>
14f719ff9bSIngo Molnar #include <linux/sched/task.h>
15f719ff9bSIngo Molnar
160226f492SAl Viro #include "proc/internal.h" /* only for get_proc_task() in ->open() */
170226f492SAl Viro
180226f492SAl Viro #include "pnode.h"
190226f492SAl Viro #include "internal.h"
200226f492SAl Viro
mounts_poll(struct file * file,poll_table * wait)21076ccb76SAl Viro static __poll_t mounts_poll(struct file *file, poll_table *wait)
220226f492SAl Viro {
23ede1bf0dSYann Droneaud struct seq_file *m = file->private_data;
24ede1bf0dSYann Droneaud struct proc_mounts *p = m->private;
250226f492SAl Viro struct mnt_namespace *ns = p->ns;
26a9a08845SLinus Torvalds __poll_t res = EPOLLIN | EPOLLRDNORM;
27aab407fcSAl Viro int event;
280226f492SAl Viro
290226f492SAl Viro poll_wait(file, &p->ns->poll, wait);
300226f492SAl Viro
316aa7de05SMark Rutland event = READ_ONCE(ns->event);
32ede1bf0dSYann Droneaud if (m->poll_event != event) {
33ede1bf0dSYann Droneaud m->poll_event = event;
34a9a08845SLinus Torvalds res |= EPOLLERR | EPOLLPRI;
350226f492SAl Viro }
360226f492SAl Viro
370226f492SAl Viro return res;
380226f492SAl Viro }
390226f492SAl Viro
401e88c420SAlexey Gladkov struct proc_fs_opts {
410226f492SAl Viro int flag;
420226f492SAl Viro const char *str;
430226f492SAl Viro };
440226f492SAl Viro
show_sb_opts(struct seq_file * m,struct super_block * sb)450226f492SAl Viro static int show_sb_opts(struct seq_file *m, struct super_block *sb)
460226f492SAl Viro {
471e88c420SAlexey Gladkov static const struct proc_fs_opts fs_opts[] = {
481751e8a6SLinus Torvalds { SB_SYNCHRONOUS, ",sync" },
491751e8a6SLinus Torvalds { SB_DIRSYNC, ",dirsync" },
501751e8a6SLinus Torvalds { SB_MANDLOCK, ",mand" },
511751e8a6SLinus Torvalds { SB_LAZYTIME, ",lazytime" },
520226f492SAl Viro { 0, NULL }
530226f492SAl Viro };
541e88c420SAlexey Gladkov const struct proc_fs_opts *fs_infop;
550226f492SAl Viro
561e88c420SAlexey Gladkov for (fs_infop = fs_opts; fs_infop->flag; fs_infop++) {
570226f492SAl Viro if (sb->s_flags & fs_infop->flag)
580226f492SAl Viro seq_puts(m, fs_infop->str);
590226f492SAl Viro }
600226f492SAl Viro
610226f492SAl Viro return security_sb_show_options(m, sb);
620226f492SAl Viro }
630226f492SAl Viro
show_mnt_opts(struct seq_file * m,struct vfsmount * mnt)640226f492SAl Viro static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt)
650226f492SAl Viro {
661e88c420SAlexey Gladkov static const struct proc_fs_opts mnt_opts[] = {
670226f492SAl Viro { MNT_NOSUID, ",nosuid" },
680226f492SAl Viro { MNT_NODEV, ",nodev" },
690226f492SAl Viro { MNT_NOEXEC, ",noexec" },
700226f492SAl Viro { MNT_NOATIME, ",noatime" },
710226f492SAl Viro { MNT_NODIRATIME, ",nodiratime" },
720226f492SAl Viro { MNT_RELATIME, ",relatime" },
73dab741e0SMattias Nissler { MNT_NOSYMFOLLOW, ",nosymfollow" },
740226f492SAl Viro { 0, NULL }
750226f492SAl Viro };
761e88c420SAlexey Gladkov const struct proc_fs_opts *fs_infop;
770226f492SAl Viro
781e88c420SAlexey Gladkov for (fs_infop = mnt_opts; fs_infop->flag; fs_infop++) {
790226f492SAl Viro if (mnt->mnt_flags & fs_infop->flag)
800226f492SAl Viro seq_puts(m, fs_infop->str);
810226f492SAl Viro }
829caccd41SChristian Brauner
83bb49e9e7SChristian Brauner if (is_idmapped_mnt(mnt))
849caccd41SChristian Brauner seq_puts(m, ",idmapped");
850226f492SAl Viro }
860226f492SAl Viro
mangle(struct seq_file * m,const char * s)870226f492SAl Viro static inline void mangle(struct seq_file *m, const char *s)
880226f492SAl Viro {
89ed5fce76SSiddhesh Poyarekar seq_escape(m, s, " \t\n\\#");
900226f492SAl Viro }
910226f492SAl Viro
show_type(struct seq_file * m,struct super_block * sb)920226f492SAl Viro static void show_type(struct seq_file *m, struct super_block *sb)
930226f492SAl Viro {
940226f492SAl Viro mangle(m, sb->s_type->name);
95c7eb6869SDavid Howells if (sb->s_subtype) {
960226f492SAl Viro seq_putc(m, '.');
970226f492SAl Viro mangle(m, sb->s_subtype);
980226f492SAl Viro }
990226f492SAl Viro }
1000226f492SAl Viro
show_vfsmnt(struct seq_file * m,struct vfsmount * mnt)1010226f492SAl Viro static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
1020226f492SAl Viro {
103ede1bf0dSYann Droneaud struct proc_mounts *p = m->private;
1040226f492SAl Viro struct mount *r = real_mount(mnt);
1050226f492SAl Viro struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
106d861c630SAl Viro struct super_block *sb = mnt_path.dentry->d_sb;
1075d9f3c7bSDmitry V. Levin int err;
1080226f492SAl Viro
109d861c630SAl Viro if (sb->s_op->show_devname) {
110d861c630SAl Viro err = sb->s_op->show_devname(m, mnt_path.dentry);
1110226f492SAl Viro if (err)
1120226f492SAl Viro goto out;
1130226f492SAl Viro } else {
1140226f492SAl Viro mangle(m, r->mnt_devname ? r->mnt_devname : "none");
1150226f492SAl Viro }
1160226f492SAl Viro seq_putc(m, ' ');
1179d4d6574SDmitry V. Levin /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
1189d4d6574SDmitry V. Levin err = seq_path_root(m, &mnt_path, &p->root, " \t\n\\");
1199d4d6574SDmitry V. Levin if (err)
1209d4d6574SDmitry V. Levin goto out;
1210226f492SAl Viro seq_putc(m, ' ');
122d861c630SAl Viro show_type(m, sb);
1230226f492SAl Viro seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw");
124d861c630SAl Viro err = show_sb_opts(m, sb);
1250226f492SAl Viro if (err)
1260226f492SAl Viro goto out;
1270226f492SAl Viro show_mnt_opts(m, mnt);
128d861c630SAl Viro if (sb->s_op->show_options)
12934c80b1dSAl Viro err = sb->s_op->show_options(m, mnt_path.dentry);
1300226f492SAl Viro seq_puts(m, " 0 0\n");
1310226f492SAl Viro out:
1320226f492SAl Viro return err;
1330226f492SAl Viro }
1340226f492SAl Viro
show_mountinfo(struct seq_file * m,struct vfsmount * mnt)1350226f492SAl Viro static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
1360226f492SAl Viro {
137ede1bf0dSYann Droneaud struct proc_mounts *p = m->private;
1380226f492SAl Viro struct mount *r = real_mount(mnt);
1390226f492SAl Viro struct super_block *sb = mnt->mnt_sb;
1400226f492SAl Viro struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
1416ce4bca0SDmitry V. Levin int err;
1420226f492SAl Viro
1430226f492SAl Viro seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id,
1440226f492SAl Viro MAJOR(sb->s_dev), MINOR(sb->s_dev));
1456ce4bca0SDmitry V. Levin if (sb->s_op->show_path) {
146a6322de6SAl Viro err = sb->s_op->show_path(m, mnt->mnt_root);
1470226f492SAl Viro if (err)
1480226f492SAl Viro goto out;
1496ce4bca0SDmitry V. Levin } else {
1506ce4bca0SDmitry V. Levin seq_dentry(m, mnt->mnt_root, " \t\n\\");
1516ce4bca0SDmitry V. Levin }
1520226f492SAl Viro seq_putc(m, ' ');
1530226f492SAl Viro
1540226f492SAl Viro /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
1559ad4dc4fSDmitry V. Levin err = seq_path_root(m, &mnt_path, &p->root, " \t\n\\");
1560226f492SAl Viro if (err)
1570226f492SAl Viro goto out;
1580226f492SAl Viro
1590226f492SAl Viro seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw");
1600226f492SAl Viro show_mnt_opts(m, mnt);
1610226f492SAl Viro
1620226f492SAl Viro /* Tagged fields ("foo:X" or "bar") */
1630226f492SAl Viro if (IS_MNT_SHARED(r))
1640226f492SAl Viro seq_printf(m, " shared:%i", r->mnt_group_id);
1650226f492SAl Viro if (IS_MNT_SLAVE(r)) {
1660226f492SAl Viro int master = r->mnt_master->mnt_group_id;
1670226f492SAl Viro int dom = get_dominating_id(r, &p->root);
1680226f492SAl Viro seq_printf(m, " master:%i", master);
1690226f492SAl Viro if (dom && dom != master)
1700226f492SAl Viro seq_printf(m, " propagate_from:%i", dom);
1710226f492SAl Viro }
1720226f492SAl Viro if (IS_MNT_UNBINDABLE(r))
1730226f492SAl Viro seq_puts(m, " unbindable");
1740226f492SAl Viro
1750226f492SAl Viro /* Filesystem specific data */
1760226f492SAl Viro seq_puts(m, " - ");
1770226f492SAl Viro show_type(m, sb);
1780226f492SAl Viro seq_putc(m, ' ');
1796ce4bca0SDmitry V. Levin if (sb->s_op->show_devname) {
180d861c630SAl Viro err = sb->s_op->show_devname(m, mnt->mnt_root);
1810226f492SAl Viro if (err)
1820226f492SAl Viro goto out;
1836ce4bca0SDmitry V. Levin } else {
1846ce4bca0SDmitry V. Levin mangle(m, r->mnt_devname ? r->mnt_devname : "none");
1856ce4bca0SDmitry V. Levin }
186bc98a42cSDavid Howells seq_puts(m, sb_rdonly(sb) ? " ro" : " rw");
1870226f492SAl Viro err = show_sb_opts(m, sb);
1880226f492SAl Viro if (err)
1890226f492SAl Viro goto out;
1900226f492SAl Viro if (sb->s_op->show_options)
19134c80b1dSAl Viro err = sb->s_op->show_options(m, mnt->mnt_root);
1920226f492SAl Viro seq_putc(m, '\n');
1930226f492SAl Viro out:
1940226f492SAl Viro return err;
1950226f492SAl Viro }
1960226f492SAl Viro
show_vfsstat(struct seq_file * m,struct vfsmount * mnt)1970226f492SAl Viro static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt)
1980226f492SAl Viro {
199ede1bf0dSYann Droneaud struct proc_mounts *p = m->private;
2000226f492SAl Viro struct mount *r = real_mount(mnt);
2010226f492SAl Viro struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
20264132379SAl Viro struct super_block *sb = mnt_path.dentry->d_sb;
203b896fb35SDmitry V. Levin int err;
2040226f492SAl Viro
2050226f492SAl Viro /* device */
20664132379SAl Viro if (sb->s_op->show_devname) {
2070226f492SAl Viro seq_puts(m, "device ");
208d861c630SAl Viro err = sb->s_op->show_devname(m, mnt_path.dentry);
2095f8d498dSDmitry V. Levin if (err)
2105f8d498dSDmitry V. Levin goto out;
2110226f492SAl Viro } else {
2120226f492SAl Viro if (r->mnt_devname) {
2130226f492SAl Viro seq_puts(m, "device ");
2140226f492SAl Viro mangle(m, r->mnt_devname);
2150226f492SAl Viro } else
2160226f492SAl Viro seq_puts(m, "no device");
2170226f492SAl Viro }
2180226f492SAl Viro
2190226f492SAl Viro /* mount point */
2200226f492SAl Viro seq_puts(m, " mounted on ");
2219d4d6574SDmitry V. Levin /* mountpoints outside of chroot jail will give SEQ_SKIP on this */
2229d4d6574SDmitry V. Levin err = seq_path_root(m, &mnt_path, &p->root, " \t\n\\");
2239d4d6574SDmitry V. Levin if (err)
2249d4d6574SDmitry V. Levin goto out;
2250226f492SAl Viro seq_putc(m, ' ');
2260226f492SAl Viro
2270226f492SAl Viro /* file system type */
2280226f492SAl Viro seq_puts(m, "with fstype ");
22964132379SAl Viro show_type(m, sb);
2300226f492SAl Viro
2310226f492SAl Viro /* optional statistics */
23264132379SAl Viro if (sb->s_op->show_stats) {
2330226f492SAl Viro seq_putc(m, ' ');
23464132379SAl Viro err = sb->s_op->show_stats(m, mnt_path.dentry);
2350226f492SAl Viro }
2360226f492SAl Viro
2370226f492SAl Viro seq_putc(m, '\n');
2389d4d6574SDmitry V. Levin out:
2390226f492SAl Viro return err;
2400226f492SAl Viro }
2410226f492SAl Viro
mounts_open_common(struct inode * inode,struct file * file,int (* show)(struct seq_file *,struct vfsmount *))2420226f492SAl Viro static int mounts_open_common(struct inode *inode, struct file *file,
2430226f492SAl Viro int (*show)(struct seq_file *, struct vfsmount *))
2440226f492SAl Viro {
2450226f492SAl Viro struct task_struct *task = get_proc_task(inode);
2460226f492SAl Viro struct nsproxy *nsp;
2470226f492SAl Viro struct mnt_namespace *ns = NULL;
2480226f492SAl Viro struct path root;
2490226f492SAl Viro struct proc_mounts *p;
250ede1bf0dSYann Droneaud struct seq_file *m;
2510226f492SAl Viro int ret = -EINVAL;
2520226f492SAl Viro
2530226f492SAl Viro if (!task)
2540226f492SAl Viro goto err;
2550226f492SAl Viro
256728dba3aSEric W. Biederman task_lock(task);
257728dba3aSEric W. Biederman nsp = task->nsproxy;
2583d93116cSAxel Lin if (!nsp || !nsp->mnt_ns) {
259728dba3aSEric W. Biederman task_unlock(task);
2600226f492SAl Viro put_task_struct(task);
2610226f492SAl Viro goto err;
2620226f492SAl Viro }
2630226f492SAl Viro ns = nsp->mnt_ns;
2640226f492SAl Viro get_mnt_ns(ns);
2650226f492SAl Viro if (!task->fs) {
2660226f492SAl Viro task_unlock(task);
2670226f492SAl Viro put_task_struct(task);
2680226f492SAl Viro ret = -ENOENT;
2690226f492SAl Viro goto err_put_ns;
2700226f492SAl Viro }
2710226f492SAl Viro get_fs_root(task->fs, &root);
2720226f492SAl Viro task_unlock(task);
2730226f492SAl Viro put_task_struct(task);
2740226f492SAl Viro
275ede1bf0dSYann Droneaud ret = seq_open_private(file, &mounts_op, sizeof(struct proc_mounts));
276ede1bf0dSYann Droneaud if (ret)
2770226f492SAl Viro goto err_put_path;
2780226f492SAl Viro
279ede1bf0dSYann Droneaud m = file->private_data;
280ede1bf0dSYann Droneaud m->poll_event = ns->event;
2810226f492SAl Viro
282ede1bf0dSYann Droneaud p = m->private;
2830226f492SAl Viro p->ns = ns;
2840226f492SAl Viro p->root = root;
2850226f492SAl Viro p->show = show;
2869f6c61f9SMiklos Szeredi INIT_LIST_HEAD(&p->cursor.mnt_list);
2879f6c61f9SMiklos Szeredi p->cursor.mnt.mnt_flags = MNT_CURSOR;
2880226f492SAl Viro
2890226f492SAl Viro return 0;
2900226f492SAl Viro
2910226f492SAl Viro err_put_path:
2920226f492SAl Viro path_put(&root);
2930226f492SAl Viro err_put_ns:
2940226f492SAl Viro put_mnt_ns(ns);
2950226f492SAl Viro err:
2960226f492SAl Viro return ret;
2970226f492SAl Viro }
2980226f492SAl Viro
mounts_release(struct inode * inode,struct file * file)2990226f492SAl Viro static int mounts_release(struct inode *inode, struct file *file)
3000226f492SAl Viro {
301ede1bf0dSYann Droneaud struct seq_file *m = file->private_data;
302ede1bf0dSYann Droneaud struct proc_mounts *p = m->private;
3030226f492SAl Viro path_put(&p->root);
3049f6c61f9SMiklos Szeredi mnt_cursor_del(p->ns, &p->cursor);
3050226f492SAl Viro put_mnt_ns(p->ns);
306ede1bf0dSYann Droneaud return seq_release_private(inode, file);
3070226f492SAl Viro }
3080226f492SAl Viro
mounts_open(struct inode * inode,struct file * file)3090226f492SAl Viro static int mounts_open(struct inode *inode, struct file *file)
3100226f492SAl Viro {
3110226f492SAl Viro return mounts_open_common(inode, file, show_vfsmnt);
3120226f492SAl Viro }
3130226f492SAl Viro
mountinfo_open(struct inode * inode,struct file * file)3140226f492SAl Viro static int mountinfo_open(struct inode *inode, struct file *file)
3150226f492SAl Viro {
3160226f492SAl Viro return mounts_open_common(inode, file, show_mountinfo);
3170226f492SAl Viro }
3180226f492SAl Viro
mountstats_open(struct inode * inode,struct file * file)3190226f492SAl Viro static int mountstats_open(struct inode *inode, struct file *file)
3200226f492SAl Viro {
3210226f492SAl Viro return mounts_open_common(inode, file, show_vfsstat);
3220226f492SAl Viro }
3230226f492SAl Viro
3240226f492SAl Viro const struct file_operations proc_mounts_operations = {
3250226f492SAl Viro .open = mounts_open,
32614e3e989SLinus Torvalds .read_iter = seq_read_iter,
327*b0072734SDavid Howells .splice_read = copy_splice_read,
3280226f492SAl Viro .llseek = seq_lseek,
3290226f492SAl Viro .release = mounts_release,
3300226f492SAl Viro .poll = mounts_poll,
3310226f492SAl Viro };
3320226f492SAl Viro
3330226f492SAl Viro const struct file_operations proc_mountinfo_operations = {
3340226f492SAl Viro .open = mountinfo_open,
33514e3e989SLinus Torvalds .read_iter = seq_read_iter,
336*b0072734SDavid Howells .splice_read = copy_splice_read,
3370226f492SAl Viro .llseek = seq_lseek,
3380226f492SAl Viro .release = mounts_release,
3390226f492SAl Viro .poll = mounts_poll,
3400226f492SAl Viro };
3410226f492SAl Viro
3420226f492SAl Viro const struct file_operations proc_mountstats_operations = {
3430226f492SAl Viro .open = mountstats_open,
34414e3e989SLinus Torvalds .read_iter = seq_read_iter,
345*b0072734SDavid Howells .splice_read = copy_splice_read,
3460226f492SAl Viro .llseek = seq_lseek,
3470226f492SAl Viro .release = mounts_release,
3480226f492SAl Viro };
349