aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linprocfs/linprocfs.c
diff options
context:
space:
mode:
authorConrad Meyer <cem@FreeBSD.org>2020-11-17 19:56:47 +0000
committerConrad Meyer <cem@FreeBSD.org>2020-11-17 19:56:47 +0000
commitb1976ea14ce2af4ac623d113d4e1f7ec57903327 (patch)
tree381f0c5964345ea57e4e3ba1658c0377ff50df25 /sys/compat/linprocfs/linprocfs.c
parent77eb9841471345a729ebbd0902c1eee08b18b176 (diff)
linprocfs(5): Add rudimentary /proc/<pid>/mountinfo
This is used by some Linux programs using filehandles (r367773) to locate the mountpoint for a given fsid. Differential Revision: https://reviews.freebsd.org/D27136
Notes
Notes: svn path=/head/; revision=367775
Diffstat (limited to 'sys/compat/linprocfs/linprocfs.c')
-rw-r--r--sys/compat/linprocfs/linprocfs.c206
1 files changed, 163 insertions, 43 deletions
diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c
index 1386b5dbe2fc..149598583159 100644
--- a/sys/compat/linprocfs/linprocfs.c
+++ b/sys/compat/linprocfs/linprocfs.c
@@ -403,24 +403,85 @@ linprocfs_docpuinfo(PFS_FILL_ARGS)
}
#endif /* __i386__ || __amd64__ */
+static const char *path_slash_sys = "/sys";
+static const char *fstype_sysfs = "sysfs";
+
+static int
+_mtab_helper(const struct pfs_node *pn, const struct statfs *sp,
+ const char **mntfrom, const char **mntto, const char **fstype)
+{
+ /* determine device name */
+ *mntfrom = sp->f_mntfromname;
+
+ /* determine mount point */
+ *mntto = sp->f_mntonname;
+
+ /* determine fs type */
+ *fstype = sp->f_fstypename;
+ if (strcmp(*fstype, pn->pn_info->pi_name) == 0)
+ *mntfrom = *fstype = "proc";
+ else if (strcmp(*fstype, "procfs") == 0)
+ return (ECANCELED);
+
+ if (strcmp(*fstype, "autofs") == 0) {
+ /*
+ * FreeBSD uses eg "map -hosts", whereas Linux
+ * expects just "-hosts".
+ */
+ if (strncmp(*mntfrom, "map ", 4) == 0)
+ *mntfrom += 4;
+ }
+
+ if (strcmp(*fstype, "linsysfs") == 0) {
+ *mntfrom = path_slash_sys;
+ *fstype = fstype_sysfs;
+ } else {
+ /* For Linux msdosfs is called vfat */
+ if (strcmp(*fstype, "msdosfs") == 0)
+ *fstype = "vfat";
+ }
+ return (0);
+}
+
+static void
+_sbuf_mntoptions_helper(struct sbuf *sb, uint64_t f_flags)
+{
+ sbuf_cat(sb, (f_flags & MNT_RDONLY) ? "ro" : "rw");
+#define ADD_OPTION(opt, name) \
+ if (f_flags & (opt)) sbuf_cat(sb, "," name);
+ ADD_OPTION(MNT_SYNCHRONOUS, "sync");
+ ADD_OPTION(MNT_NOEXEC, "noexec");
+ ADD_OPTION(MNT_NOSUID, "nosuid");
+ ADD_OPTION(MNT_UNION, "union");
+ ADD_OPTION(MNT_ASYNC, "async");
+ ADD_OPTION(MNT_SUIDDIR, "suiddir");
+ ADD_OPTION(MNT_NOSYMFOLLOW, "nosymfollow");
+ ADD_OPTION(MNT_NOATIME, "noatime");
+#undef ADD_OPTION
+}
+
/*
- * Filler function for proc/mtab
+ * Filler function for proc/mtab and proc/<pid>/mounts.
*
- * This file doesn't exist in Linux' procfs, but is included here so
+ * /proc/mtab doesn't exist in Linux' procfs, but is included here so
* users can symlink /compat/linux/etc/mtab to /proc/mtab
*/
static int
linprocfs_domtab(PFS_FILL_ARGS)
{
struct nameidata nd;
- const char *lep;
- char *dlep, *flep, *mntto, *mntfrom, *fstype;
+ const char *lep, *mntto, *mntfrom, *fstype;
+ char *dlep, *flep;
size_t lep_len;
int error;
struct statfs *buf, *sp;
size_t count;
/* resolve symlinks etc. in the emulation tree prefix */
+ /*
+ * Ideally, this would use the current chroot rather than some
+ * hardcoded path.
+ */
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path, td);
flep = NULL;
error = namei(&nd);
@@ -442,55 +503,112 @@ linprocfs_domtab(PFS_FILL_ARGS)
}
for (sp = buf; count > 0; sp++, count--) {
- /* determine device name */
- mntfrom = sp->f_mntfromname;
+ error = _mtab_helper(pn, sp, &mntfrom, &mntto, &fstype);
+ if (error != 0) {
+ MPASS(error == ECANCELED);
+ continue;
+ }
/* determine mount point */
- mntto = sp->f_mntonname;
if (strncmp(mntto, lep, lep_len) == 0 && mntto[lep_len] == '/')
mntto += lep_len;
- /* determine fs type */
- fstype = sp->f_fstypename;
- if (strcmp(fstype, pn->pn_info->pi_name) == 0)
- mntfrom = fstype = "proc";
- else if (strcmp(fstype, "procfs") == 0)
- continue;
+ sbuf_printf(sb, "%s %s %s ", mntfrom, mntto, fstype);
+ _sbuf_mntoptions_helper(sb, sp->f_flags);
+ /* a real Linux mtab will also show NFS options */
+ sbuf_printf(sb, " 0 0\n");
+ }
- if (strcmp(fstype, "autofs") == 0) {
- /*
- * FreeBSD uses eg "map -hosts", whereas Linux
- * expects just "-hosts".
- */
- if (strncmp(mntfrom, "map ", 4) == 0)
- mntfrom += 4;
- }
+ free(buf, M_TEMP);
+ free(flep, M_TEMP);
+ return (error);
+}
- if (strcmp(fstype, "linsysfs") == 0) {
- sbuf_printf(sb, "/sys %s sysfs %s", mntto,
- sp->f_flags & MNT_RDONLY ? "ro" : "rw");
- } else {
- /* For Linux msdosfs is called vfat */
- if (strcmp(fstype, "msdosfs") == 0)
- fstype = "vfat";
- sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype,
- sp->f_flags & MNT_RDONLY ? "ro" : "rw");
+static int
+linprocfs_doprocmountinfo(PFS_FILL_ARGS)
+{
+ struct nameidata nd;
+ const char *mntfrom, *mntto, *fstype;
+ const char *lep;
+ char *dlep, *flep;
+ struct statfs *buf, *sp;
+ size_t count, lep_len;
+ int error;
+
+ /*
+ * Ideally, this would use the current chroot rather than some
+ * hardcoded path.
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path, td);
+ flep = NULL;
+ error = namei(&nd);
+ lep = linux_emul_path;
+ if (error == 0) {
+ if (vn_fullpath(nd.ni_vp, &dlep, &flep) == 0)
+ lep = dlep;
+ vrele(nd.ni_vp);
+ }
+ lep_len = strlen(lep);
+
+ buf = NULL;
+ error = kern_getfsstat(td, &buf, SIZE_T_MAX, &count,
+ UIO_SYSSPACE, MNT_WAIT);
+ if (error != 0)
+ goto out;
+
+ for (sp = buf; count > 0; sp++, count--) {
+ error = _mtab_helper(pn, sp, &mntfrom, &mntto, &fstype);
+ if (error != 0) {
+ MPASS(error == ECANCELED);
+ continue;
}
-#define ADD_OPTION(opt, name) \
- if (sp->f_flags & (opt)) sbuf_printf(sb, "," name);
- ADD_OPTION(MNT_SYNCHRONOUS, "sync");
- ADD_OPTION(MNT_NOEXEC, "noexec");
- ADD_OPTION(MNT_NOSUID, "nosuid");
- ADD_OPTION(MNT_UNION, "union");
- ADD_OPTION(MNT_ASYNC, "async");
- ADD_OPTION(MNT_SUIDDIR, "suiddir");
- ADD_OPTION(MNT_NOSYMFOLLOW, "nosymfollow");
- ADD_OPTION(MNT_NOATIME, "noatime");
-#undef ADD_OPTION
- /* a real Linux mtab will also show NFS options */
- sbuf_printf(sb, " 0 0\n");
+
+ if (strncmp(mntto, lep, lep_len) == 0 && mntto[lep_len] == '/')
+ mntto += lep_len;
+#if 0
+ /*
+ * If the prefix is a chroot, and this mountpoint is not under
+ * the prefix, we should skip it. Leave it for now for
+ * consistency with procmtab above.
+ */
+ else
+ continue;
+#endif
+
+ /*
+ * (1) mount id
+ *
+ * (2) parent mount id -- we don't have this cheaply, so
+ * provide a dummy value
+ *
+ * (3) major:minor -- ditto
+ *
+ * (4) root filesystem mount -- probably a namespaces thing
+ *
+ * (5) mountto path
+ */
+ sbuf_printf(sb, "%u 0 0:0 / %s ",
+ sp->f_fsid.val[0] ^ sp->f_fsid.val[1], mntto);
+ /* (6) mount options */
+ _sbuf_mntoptions_helper(sb, sp->f_flags);
+ /*
+ * (7) zero or more optional fields -- again, namespace related
+ *
+ * (8) End of variable length fields separator ("-")
+ *
+ * (9) fstype
+ *
+ * (10) mount from
+ *
+ * (11) "superblock" options -- like (6), but different
+ * semantics in Linux
+ */
+ sbuf_printf(sb, " - %s %s %s\n", fstype, mntfrom,
+ (sp->f_flags & MNT_RDONLY) ? "ro" : "rw");
}
+ error = 0;
+out:
free(buf, M_TEMP);
free(flep, M_TEMP);
return (error);
@@ -1889,6 +2007,8 @@ linprocfs_init(PFS_INIT_ARGS)
NULL, NULL, NULL, PFS_RD | PFS_AUTODRAIN);
pfs_create_file(dir, "mem", &linprocfs_doprocmem,
procfs_attr_rw, &procfs_candebug, NULL, PFS_RDWR | PFS_RAW);
+ pfs_create_file(dir, "mountinfo", &linprocfs_doprocmountinfo,
+ NULL, NULL, NULL, PFS_RD);
pfs_create_file(dir, "mounts", &linprocfs_domtab,
NULL, NULL, NULL, PFS_RD);
pfs_create_link(dir, "root", &linprocfs_doprocroot,