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
This commit is contained in:
Conrad Meyer 2020-11-17 19:56:47 +00:00
parent 77eb984147
commit b1976ea14c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=367775

View File

@ -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,51 +503,18 @@ 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;
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) {
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");
}
#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
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");
}
@ -496,6 +524,96 @@ linprocfs_domtab(PFS_FILL_ARGS)
return (error);
}
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;
}
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);
}
/*
* Filler function for proc/partitions
*/
@ -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,