Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 17 Nov 2020 19:56:48 +0000 (UTC)
From:      Conrad Meyer <cem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r367775 - head/sys/compat/linprocfs
Message-ID:  <202011171956.0AHJumRE017643@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: cem
Date: Tue Nov 17 19:56:47 2020
New Revision: 367775
URL: https://svnweb.freebsd.org/changeset/base/367775

Log:
  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

Modified:
  head/sys/compat/linprocfs/linprocfs.c

Modified: head/sys/compat/linprocfs/linprocfs.c
==============================================================================
--- head/sys/compat/linprocfs/linprocfs.c	Tue Nov 17 19:53:59 2020	(r367774)
+++ head/sys/compat/linprocfs/linprocfs.c	Tue Nov 17 19:56:47 2020	(r367775)
@@ -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,



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202011171956.0AHJumRE017643>