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,
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to