summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2016-08-19 20:04:28 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2016-08-19 20:12:06 +0000
commitfa4a6a633d447e433f22c28800fcd967975ef403 (patch)
treea4d73ca8422759127a6ece1bae27e29549036fff /usr/src
parent533b6005f11009bfbddd0c485c45a745d6ca290f (diff)
downloadillumos-joyent-fa4a6a633d447e433f22c28800fcd967975ef403.tar.gz
OS-5545 lxbrand move mount(2) emulation into kernel
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com> Reviewed by: Ryan Zezeski <ryan.zezeski@joyent.com>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/lx_brand.c6
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/mount.c691
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h2
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_syscall.c10
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_syscalls.h6
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_mount.c650
-rw-r--r--usr/src/uts/intel/Makefile.files1
7 files changed, 733 insertions, 633 deletions
diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
index e85c19e6dd..274d5435a4 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
@@ -1177,7 +1177,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 163: acct */
lx_settimeofday, /* 164: settimeofday */
lx_mount, /* 165: mount */
- lx_umount2, /* 166: umount2 */
+ NULL, /* 166: umount2 */
NULL, /* 167: swapon */
NULL, /* 168: swapoff */
lx_reboot, /* 169: reboot */
@@ -1364,7 +1364,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 19: lseek */
NULL, /* 20: getpid */
lx_mount, /* 21: mount */
- lx_umount, /* 22: umount */
+ NULL, /* 22: umount */
lx_setuid16, /* 23: setuid16 */
lx_getuid16, /* 24: getuid16 */
lx_stime, /* 25: stime */
@@ -1394,7 +1394,7 @@ static lx_syscall_handler_t lx_handlers[] = {
lx_geteuid16, /* 49: geteuid16 */
lx_getegid16, /* 50: getegid16 */
NULL, /* 51: acct */
- lx_umount2, /* 52: umount2 */
+ NULL, /* 52: umount2 */
NULL, /* 53: lock */
NULL, /* 54: ioctl */
NULL, /* 55: fcntl */
diff --git a/usr/src/lib/brand/lx/lx_brand/common/mount.c b/usr/src/lib/brand/lx/lx_brand/common/mount.c
index 8416b6f0ee..d06379555a 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/mount.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/mount.c
@@ -25,24 +25,16 @@
* Copyright 2016 Joyent, Inc.
*/
-#include <alloca.h>
#include <assert.h>
-#include <ctype.h>
-#include <fcntl.h>
#include <errno.h>
-#include <signal.h>
-#include <string.h>
#include <strings.h>
#include <nfs/mount.h>
#include <sys/types.h>
#include <sys/mount.h>
-#include <sys/param.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
-#include <sys/lx_autofs.h>
#include <sys/lx_debug.h>
#include <sys/lx_misc.h>
#include <sys/lx_syscall.h>
@@ -57,322 +49,6 @@ union fh_buffer {
char fh_data[NFS3_FHSIZE + 2];
};
-typedef enum mount_opt_type {
- MOUNT_OPT_INVALID = 0,
- MOUNT_OPT_NORMAL = 1, /* option value: none */
- MOUNT_OPT_UINT = 2, /* option value: unsigned int */
- MOUNT_OPT_BYTESIZE = 3 /* option value: byte size, e.g. 25m */
-} mount_opt_type_t;
-
-typedef struct mount_opt {
- char *mo_name;
- mount_opt_type_t mo_type;
-} mount_opt_t;
-
-
-/*
- * Globals
- */
-mount_opt_t lofs_options[] = {
- { NULL, MOUNT_OPT_INVALID }
-};
-
-mount_opt_t lx_proc_options[] = {
- { NULL, MOUNT_OPT_INVALID }
-};
-
-mount_opt_t lx_sysfs_options[] = {
- { NULL, MOUNT_OPT_INVALID }
-};
-
-mount_opt_t lx_tmpfs_options[] = {
- { "size", MOUNT_OPT_BYTESIZE },
- { "mode", MOUNT_OPT_UINT },
- { "uid", MOUNT_OPT_UINT },
- { "gid", MOUNT_OPT_UINT },
- { NULL, MOUNT_OPT_INVALID }
-};
-
-mount_opt_t lx_autofs_options[] = {
- { LX_MNTOPT_FD, MOUNT_OPT_UINT },
- { LX_MNTOPT_PGRP, MOUNT_OPT_UINT },
- { LX_MNTOPT_MINPROTO, MOUNT_OPT_UINT },
- { LX_MNTOPT_MAXPROTO, MOUNT_OPT_UINT },
- { LX_MNTOPT_INDIRECT, MOUNT_OPT_NORMAL },
- { LX_MNTOPT_DIRECT, MOUNT_OPT_NORMAL },
- { LX_MNTOPT_OFFSET, MOUNT_OPT_NORMAL },
- { NULL, MOUNT_OPT_INVALID }
-};
-
-
-/*
- * i_lx_opt_verify() - Check the mount options.
- *
- * You might wonder why we're being so strict about the mount options
- * we allow. The reason is that normally all mount option verification
- * is done by the Solaris userland mount command. Once mount options
- * are passed to the kernel, invalid options are simply ignored. So
- * if we actually want to catch requests for functionality that we
- * don't support, or if we want to make sure that we don't randomly
- * enable options that we haven't check to make sure they have the
- * same syntax on Linux and Solaris, we need to reject any options
- * we don't know to be ok here.
- */
-static int
-i_lx_opt_verify(char *opts, mount_opt_t *mop)
-{
- int opts_len = strlen(opts);
- char *opts_tmp, *opt;
- int opt_len, i;
-
- assert((opts != NULL) && (mop != NULL));
-
- /* If no options were specified, there's no problem. */
- if (opts_len == 0) {
- errno = 0;
- return (0);
- }
-
- /* If no options are allowed, fail. */
- if (mop[0].mo_name == NULL) {
- errno = ENOTSUP;
- return (-1);
- }
-
- /* Don't accept leading or trailing ','. */
- if ((opts[0] == ',') || (opts[opts_len] == ',')) {
- errno = EINVAL;
- return (-1);
- }
-
- /* Don't accept sequential ','. */
- for (i = 1; i < opts_len; i++) {
- if ((opts[i - 1] == ',') && (opts[i] == ',')) {
- errno = EINVAL;
- return (-1);
- }
- }
-
- /*
- * We're going to use strtok() which modifies the target
- * string so make a temporary copy.
- */
- opts_tmp = SAFE_ALLOCA(opts_len);
- if (opts_tmp == NULL) {
- errno = ENOMEM;
- return (-1);
- }
- bcopy(opts, opts_tmp, opts_len + 1);
-
- /* Verify each prop one at a time. */
- opt = strtok(opts_tmp, ",");
- opt_len = strlen(opt);
- for (;;) {
-
- /* Check for matching option/value pair. */
- for (i = 0; mop[i].mo_name != NULL; i++) {
- char *ovalue;
- int ovalue_len, mo_len;
-
- /* If the options is too short don't bother comparing */
- mo_len = strlen(mop[i].mo_name);
- if (opt_len < mo_len) {
- /* Keep trying to find a match. */
- continue;
- }
-
- /* Compare the option to an allowed option. */
- if (strncmp(mop[i].mo_name, opt, mo_len) != 0) {
- /* Keep trying to find a match. */
- continue;
- }
-
- if (mop[i].mo_type == MOUNT_OPT_NORMAL) {
- /* The option doesn't take a value. */
- if (opt_len == mo_len) {
- /* This option is ok. */
- break;
- } else {
- /* Keep trying to find a match. */
- continue;
- }
- }
-
- /* This options takes a value. */
- if ((opt_len == mo_len) || (opt[mo_len] != '=')) {
- /* Keep trying to find a match. */
- continue;
- }
-
- /* We have an option match. Verify option value. */
- ovalue = &opt[mo_len] + 1;
- ovalue_len = strlen(ovalue);
-
- /* Value can't be zero length string. */
- if (ovalue_len == 0) {
- errno = EINVAL;
- return (-1);
- }
-
- if (mop[i].mo_type == MOUNT_OPT_UINT) {
- int j;
- /* Verify that value is an unsigned int. */
- for (j = 0; j < ovalue_len; j++) {
- if (!isdigit(ovalue[j])) {
- errno = EINVAL;
- return (-1);
- }
- }
- } else if (mop[i].mo_type == MOUNT_OPT_BYTESIZE) {
- int j;
- int stage = 0;
- int suffix;
-
- /*
- * Verify that the value is an unsigned integer
- * that ends in a magnitude suffix (i.e. case
- * insensitive 'k' 'm' or 'g') or a '%'
- * character.
- */
- for (j = 0; j < ovalue_len; j++) {
- switch (stage) {
- case 0:
- /*
- * Look for at least one digit.
- */
- if (!isdigit(ovalue[j])) {
- errno = EINVAL;
- return (-1);
- }
- stage = 1;
- break;
- case 1:
- /*
- * Allow an unlimited number of
- * digits.
- */
- if (isdigit(ovalue[j])) {
- break;
- }
- /*
- * Allow one (valid) byte
- * magnitude character.
- */
- suffix = tolower(ovalue[j]);
- if (suffix == 'k' ||
- suffix == 'm' ||
- suffix == 'g' ||
- suffix == '%') {
- stage = 2;
- break;
- }
- errno = EINVAL;
- return (-1);
- case 2:
- /*
- * Invalid trailing characters.
- */
- errno = EINVAL;
- return (-1);
- }
- }
-
- if (stage < 1) {
- errno = EINVAL;
- return (-1);
- }
- } else {
- /* Unknown option type specified. */
- assert(0);
- }
-
- /* The option is ok. */
- break;
- }
-
- /* If there were no matches this is an unsupported option. */
- if (mop[i].mo_name == NULL) {
- errno = EINVAL;
- return (-1);
- }
-
- /* This option is ok, move onto the next option. */
- if ((opt = strtok(NULL, ",")) == NULL)
- break;
- opt_len = strlen(opt);
- };
-
- /* We verified all the options. */
- return (0);
-}
-
-/*
- * Remove an option from the string and save it in the provided buffer.
- * The option string should have already been verified as valid.
- * Return 0 if not present, -1 if error, and 1 if present and fine.
- */
-static int
-opt_rm(char *opts, char *rmopt, char *retstr, int retlen)
-{
- int opts_len = strlen(opts);
- char *optstart, *optend;
- int optlen;
-
- assert((opts != NULL) && (rmopt != NULL));
-
- retstr[0] = '\0';
-
- /* If no options were specified, there's no problem. */
- if (opts_len == 0)
- return (0);
-
- if ((optstart = strstr(opts, rmopt)) == NULL)
- return (0);
-
- for (optend = optstart; *optend != ',' && *optend != '\0'; optend++)
- ;
-
- optlen = optend - optstart;
- if (optlen >= retlen)
- return (-1);
- (void) strncpy(retstr, optstart, optlen);
- retstr[optlen] = '\0';
-
- if (*optend == ',')
- optend++;
-
- optlen = strlen(optend) + 1;
- bcopy(optend, optstart, optlen);
-
- if (*optstart == '\0' && optstart != opts) {
- /* removed last opt and it had a preceeding opt, remove comma */
- *(optstart - 1) = '\0';
- }
-
- return (1);
-}
-
-static int
-opt_id_val(char *opt, int *valp)
-{
- char *vp;
- long lval;
-
- if ((vp = strchr(opt, '=')) == NULL)
- return (-1);
-
- vp++;
- if (!isdigit(*vp))
- return (-1);
-
- lval = strtol(vp, &vp, 10);
- if (*vp != '\0' || lval > INT_MAX)
- return (-1);
-
- *valp = (int)lval;
- return (0);
-}
-
static int
i_add_option(char *option, char *buf, size_t buf_size)
{
@@ -675,6 +351,10 @@ i_make_nfs_args(lx_nfs_mount_data_t *lx_nmd, struct nfs_args *nfs_args,
return (0);
}
+/*
+ * The user-level mount(2) code is only used to support NFS mounts. All other
+ * fstypes are handled in-kernel.
+ */
long
lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
uintptr_t p5)
@@ -686,19 +366,12 @@ lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
unsigned int flags = (unsigned int)p4;
const void *datap = (const void *)p5;
- /* Variables needed for all mounts. */
char source[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1];
char target[MAXPATHLEN];
- char fstype[MAXPATHLEN], options[MAX_MNTOPT_STR];
+ char fstype[8], options[MAX_MNTOPT_STR];
int sflags, rv;
long res;
- boolean_t is_tmpfs = B_FALSE;
- /* Variable for tmpfs mounts. */
- int uid = -1;
- int gid = -1;
-
- /* Variables needed for nfs mounts. */
lx_nfs_mount_data_t lx_nmd;
struct nfs_args nfs_args;
struct netbuf nfs_args_addr;
@@ -707,8 +380,9 @@ lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
struct sec_data nfs_args_secdata;
char *sdataptr = NULL;
int sdatalen = 0;
+ int vers;
- /* Initialize Solaris mount arguments. */
+ /* Initialize illumos mount arguments. */
sflags = MS_OPTIONSTR;
options[0] = '\0';
sdatalen = 0;
@@ -730,6 +404,9 @@ lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
lx_debug("\tlinux mount target: %s", target);
lx_debug("\tlinux mount fstype: %s", fstype);
+ /* The in-kernel mount code should only call us for an NFS mount. */
+ assert(strcmp(fstype, "nfs") == 0);
+
/*
* While SunOS is picky about mount(2) target paths being absolute,
* Linux is not so strict. In order to facilitate this looser
@@ -760,261 +437,72 @@ lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
return (-ENOTSUP);
}
- /* Do filesystem specific mount work. */
- if (flags & LX_MS_BIND) {
-
- /* If MS_BIND is set, we turn this into a lofs mount. */
- (void) strcpy(fstype, "lofs");
-
- /* Copy in Linux mount options. */
- if (datap != NULL) {
- rv = uucopystr((void *)datap,
- options, sizeof (options));
- if ((rv == -1) || (rv == sizeof (options)))
- return (-EFAULT);
- }
- lx_debug("\tlinux mount options: \"%s\"", options);
-
- /* Verify Linux mount options. */
- if (i_lx_opt_verify(options, lofs_options) != 0) {
- lx_unsupported("unsupported lofs mount options: %s",
- options);
- return (-errno);
- }
- } else if (strcmp(fstype, "tmpfs") == 0) {
- char idstr[64];
-
- /* Copy in Linux mount options. */
- if (datap != NULL) {
- rv = uucopystr((void *)datap,
- options, sizeof (options));
- if ((rv == -1) || (rv == sizeof (options)))
- return (-EFAULT);
- }
- lx_debug("\tlinux mount options: \"%s\"", options);
-
- /* Verify Linux mount options. */
- if (i_lx_opt_verify(options, lx_tmpfs_options) != 0) {
- lx_unsupported("unsupported tmpfs mount options: %s",
- options);
- return (-errno);
- }
-
- /*
- * Linux defaults to mode=1777 for tmpfs mounts.
- */
- if (strstr(options, "mode=") == NULL) {
- if (options[0] != '\0')
- (void) strlcat(options, ",", sizeof (options));
- (void) strlcat(options, "mode=1777", sizeof (options));
- }
-
- switch (opt_rm(options, "uid=", idstr, sizeof (idstr))) {
- case 0:
- uid = -1;
- break;
- case 1:
- if (opt_id_val(idstr, &uid) < 0)
- return (-EINVAL);
- break;
- default:
- return (-E2BIG);
- }
- switch (opt_rm(options, "gid=", idstr, sizeof (idstr))) {
- case 0:
- gid = -1;
- break;
- case 1:
- if (opt_id_val(idstr, &gid) < 0)
- return (-EINVAL);
- break;
- default:
- return (-E2BIG);
- }
-
- /*
- * Linux seems to always allow overlay mounts. We allow this
- * everywhere except under /dev where it interferes with device
- * emulation.
- */
- if (strcmp(targetp, "/dev") != 0 &&
- strncmp(targetp, "/dev/", 5) != 0)
- sflags |= MS_OVERLAY;
-
- is_tmpfs = B_TRUE;
-
- } else if (strcmp(fstype, "proc") == 0) {
- struct stat64 sb;
-
- /* Translate proc mount requests to lx_proc requests. */
- (void) strcpy(fstype, "lx_proc");
-
- /* Copy in Linux mount options. */
- if (datap != NULL) {
- rv = uucopystr((void *)datap,
- options, sizeof (options));
- if ((rv == -1) || (rv == sizeof (options)))
- return (-EFAULT);
- }
- lx_debug("\tlinux mount options: \"%s\"", options);
-
- /* Verify Linux mount options. */
- if (i_lx_opt_verify(options, lx_proc_options) != 0) {
- lx_unsupported("unsupported proc mount options: %s",
- options);
- return (-errno);
- }
-
- /* If mounting proc over itself, just return ok */
- if (stat64(target, &sb) == 0 &&
- strcmp(sb.st_fstype, "lx_proc") == 0) {
- return (0);
- }
- } else if (strcmp(fstype, "sysfs") == 0) {
- /* Translate sysfs mount requests to lx_sysfs requests. */
- (void) strcpy(fstype, "lx_sysfs");
-
- /* Copy in Linux mount options. */
- if (datap != NULL) {
- rv = uucopystr((void *)datap,
- options, sizeof (options));
- if ((rv == -1) || (rv == sizeof (options)))
- return (-EFAULT);
- }
- lx_debug("\tlinux mount options: \"%s\"", options);
-
- /* Verify Linux mount options. */
- if (i_lx_opt_verify(options, lx_sysfs_options) != 0) {
- lx_unsupported("unsupported sysfs mount options: %s",
- options);
- return (-errno);
- }
- } else if (strcmp(fstype, "cgroup") == 0) {
- /* Translate cgroup mount requests to lx_cgroup requests. */
- (void) strcpy(fstype, "lx_cgroup");
-
- /* Copy in Linux mount options. */
- if (datap != NULL) {
- rv = uucopystr((void *)datap,
- options, sizeof (options));
- if ((rv == -1) || (rv == sizeof (options)))
- return (-EFAULT);
- }
- lx_debug("\tlinux mount options: \"%s\"", options);
-
- /*
- * Currently don't verify Linux mount options since we can
- * have asubsystem string provided.
- */
-
- } else if (strcmp(fstype, "autofs") == 0) {
-
- /* Translate autofs mount requests to lxautofs requests. */
- (void) strcpy(fstype, LX_AUTOFS_NAME);
-
- /* Copy in Linux mount options. */
- if (datap != NULL) {
- rv = uucopystr((void *)datap,
- options, sizeof (options));
- if ((rv == -1) || (rv == sizeof (options)))
- return (-EFAULT);
- }
- lx_debug("\tlinux mount options: \"%s\"", options);
-
- /* Verify Linux mount options. */
- if (i_lx_opt_verify(options, lx_autofs_options) != 0) {
- lx_unsupported("unsupported autofs mount options: %s",
- options);
- return (-errno);
- }
-
- /* Linux seems to always allow overlay mounts */
- sflags |= MS_OVERLAY;
-
- } else if (strcmp(fstype, "nfs") == 0) {
+ /*
+ * Copy in Linux mount options. Note that for older Linux kernels
+ * (pre 2.6.23) the mount options pointer (which normally points to a
+ * string) points to a structure which is populated by the user-level
+ * code after it has done the preliminary RPCs (similar to how our NFS
+ * mount cmd works). For newer kernels the options pointer is just a
+ * string of options. We're unlikely to actually emulate a kernel that
+ * uses the old style but support is kept and handled in
+ * i_make_nfs_args(). The new style handling is implemented in
+ * lx_nfs_mount(). The user-level mount caller is in charge of
+ * determining the format in which it passes the data parameter.
+ */
+ if (datap == NULL)
+ return (-EINVAL);
+ if (uucopy((void *)datap, &vers, sizeof (int)) < 0)
+ return (-errno);
+ /*
+ * As described above, the data parameter might be a versioned lx_nmd
+ * structure or (most likely on a modern distribution) it is a string.
+ */
+ if (vers < 1 || vers > 6) {
/*
- * Copy in Linux mount options. Note that for older Linux
- * kernels (pre 2.6.23) the mount options pointer (which
- * normally points to a string) points to a structure which
- * is populated by the user-level code after it has done the
- * preliminary RPCs (similar to how our NFS mount cmd works).
- * For newer kernels the options pointer is just a string of
- * options. We're unlikely to actually emulate a kernel that
- * uses the old style but support is kept and handled in
- * i_make_nfs_args(). The new style handling is implemented in
- * nfs_pre_mount(). The user-level mount caller is in charge of
- * determining the format in which it passes the data parameter.
+ * Handle the modern style with options as a string, make the
+ * preliminary RPC calls and do the native mount all within
+ * lx_nfs_mount().
*/
- int vers;
-
- if (datap == NULL)
- return (-EINVAL);
- if (uucopy((void *)datap, &vers, sizeof (int)) < 0)
+ if (uucopystr((void *)datap, options, sizeof (options)) < 0)
return (-errno);
+ return (lx_nfs_mount(source, target, fstype, flags, options));
+ }
- /*
- * As described above, the data parameter might be a versioned
- * lx_nmd structure or (most likely) it is just a string.
- */
- switch (vers) {
- case 1:
- case 2:
- case 3:
- case 5:
- case 6:
- lx_unsupported("unsupported nfs mount request "
- "version: %d\n", vers);
- return (-ENOTSUP);
-
- case 4:
- if (uucopy((void *)datap, &lx_nmd, sizeof (lx_nmd)) < 0)
- return (-errno);
-
- /*
- * For Illumos nfs mounts, the kernel expects a special
- * structure, but a pointer to this structure is passed
- * in via an extra parameter (sdataptr below.)
- */
- if ((rv = i_make_nfs_args(&lx_nmd, &nfs_args,
- &nfs_args_addr, &nfs_args_knconf, &nfs_args_fh,
- &nfs_args_secdata, fstype, options,
- sizeof (options))) != 0)
- return (rv);
-
- break;
+ /*
+ * This is an old style NFS mount call and we only support v4.
+ */
+ if (vers != 4) {
+ lx_unsupported("unsupported nfs mount request version: %d\n",
+ vers);
+ return (-ENOTSUP);
+ }
- default:
- /*
- * Handle new style with options as a string, make
- * the preliminary RPC calls and do the native mount
- * all within lx_nfs_mount().
- */
- if (uucopystr((void *)datap, options,
- sizeof (options)) < 0)
- return (-errno);
- return (lx_nfs_mount(source, target, fstype, flags,
- options));
- break;
- }
+ if (uucopy((void *)datap, &lx_nmd, sizeof (lx_nmd)) < 0)
+ return (-errno);
- /*
- * For nfs mounts we need to tell the mount system call
- * to expect extra parameters.
- */
- sflags |= MS_DATA;
- sdataptr = (char *)&nfs_args;
- sdatalen = sizeof (nfs_args);
+ /*
+ * For illumos NFS mounts, the kernel expects a special structure, but
+ * a pointer to this structure is passed in via an extra parameter
+ * (sdataptr below.)
+ */
+ if ((rv = i_make_nfs_args(&lx_nmd, &nfs_args, &nfs_args_addr,
+ &nfs_args_knconf, &nfs_args_fh, &nfs_args_secdata, fstype, options,
+ sizeof (options))) != 0)
+ return (rv);
- /* Linux seems to always allow overlay mounts */
- sflags |= MS_OVERLAY;
+ /*
+ * For the following old-style NFS mount we need to tell the mount
+ * system call to expect extra parameters.
+ */
+ sflags |= MS_DATA;
+ sdataptr = (char *)&nfs_args;
+ sdatalen = sizeof (nfs_args);
- } else {
- lx_unsupported("unsupported mount filesystem type: %s", fstype);
- return (-ENODEV);
- }
+ /* Linux seems to always allow overlay mounts */
+ sflags |= MS_OVERLAY;
- /* Convert some Linux flags to Illumos flags. */
+ /* Convert some Linux flags to illumos flags. */
if (flags & LX_MS_RDONLY)
sflags |= MS_RDONLY;
if (flags & LX_MS_NOSUID)
@@ -1023,7 +511,7 @@ lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
sflags |= MS_REMOUNT;
/*
- * Convert some Linux flags to Illumos option strings.
+ * Convert some Linux flags to illumos option strings.
*/
if (flags & LX_MS_STRICTATIME) {
/*
@@ -1048,48 +536,5 @@ lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
res = mount(source, target, sflags, fstype, sdataptr, sdatalen,
options, sizeof (options));
- if (res == 0) {
- if (is_tmpfs) {
- /* Handle uid/gid mount options. */
- if (uid != -1 || gid != -1)
- (void) chown(target, uid, gid);
- return (0);
-
- } else {
- return (0);
- }
- } else {
- return (-errno);
- }
-}
-
-/*
- * umount() is identical, though it is implemented on top of umount2() in
- * Solaris so it cannot be a pass-thru system call.
- */
-long
-lx_umount(uintptr_t p1)
-{
- return (umount((char *)p1) ? -errno : 0);
-}
-
-/*
- * The Linux umount2() system call is identical but has a different value for
- * MNT_FORCE (the logical equivalent to MS_FORCE).
- */
-#define LX_MNT_FORCE 0x1
-
-long
-lx_umount2(uintptr_t p1, uintptr_t p2)
-{
- char *path = (char *)p1;
- int flags = 0;
-
- if (p2 & ~LX_MNT_FORCE)
- return (-EINVAL);
-
- if (p2 & LX_MNT_FORCE)
- flags |= MS_FORCE;
-
- return (umount2(path, flags) ? -errno : 0);
+ return ((res == 0) ? 0 : -errno);
}
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
index 8369298514..b1cdba338f 100644
--- a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
@@ -159,8 +159,6 @@ extern long lx_mmap2(uintptr_t, uintptr_t, uintptr_t, uintptr_t,
extern long lx_remap(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern long lx_mount(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
-extern long lx_umount(uintptr_t);
-extern long lx_umount2(uintptr_t, uintptr_t);
extern long lx_statfs(uintptr_t, uintptr_t);
extern long lx_fstatfs(uintptr_t, uintptr_t);
diff --git a/usr/src/uts/common/brand/lx/os/lx_syscall.c b/usr/src/uts/common/brand/lx/os/lx_syscall.c
index f360225974..5d4b10a012 100644
--- a/usr/src/uts/common/brand/lx/os/lx_syscall.c
+++ b/usr/src/uts/common/brand/lx/os/lx_syscall.c
@@ -542,8 +542,8 @@ lx_sysent_t lx_sysent32[] = {
{"stat", NULL, NOSYS_OBSOLETE, 0}, /* 18 */
{"lseek", lx_lseek32, 0, 3}, /* 19 */
{"getpid", lx_getpid, 0, 0}, /* 20 */
- {"mount", NULL, 0, 5}, /* 21 */
- {"umount", NULL, 0, 1}, /* 22 */
+ {"mount", lx_mount, 0, 5}, /* 21 */
+ {"umount", lx_umount, 0, 1}, /* 22 */
{"setuid16", NULL, 0, 1}, /* 23 */
{"getuid16", NULL, 0, 0}, /* 24 */
{"stime", NULL, 0, 1}, /* 25 */
@@ -573,7 +573,7 @@ lx_sysent_t lx_sysent32[] = {
{"geteuid16", NULL, 0, 0}, /* 49 */
{"getegid16", NULL, 0, 0}, /* 50 */
{"acct", NULL, NOSYS_NO_EQUIV, 0}, /* 51 */
- {"umount2", NULL, 0, 2}, /* 52 */
+ {"umount2", lx_umount2, 0, 2}, /* 52 */
{"lock", NULL, NOSYS_OBSOLETE, 0}, /* 53 */
{"ioctl", lx_ioctl, 0, 3}, /* 54 */
{"fcntl", lx_fcntl, 0, 3}, /* 55 */
@@ -1057,8 +1057,8 @@ lx_sysent_t lx_sysent64[] = {
{"sync", NULL, 0, 0}, /* 162 */
{"acct", NULL, NOSYS_NO_EQUIV, 0}, /* 163 */
{"settimeofday", NULL, 0, 2}, /* 164 */
- {"mount", NULL, 0, 5}, /* 165 */
- {"umount2", NULL, 0, 2}, /* 166 */
+ {"mount", lx_mount, 0, 5}, /* 165 */
+ {"umount2", lx_umount2, 0, 2}, /* 166 */
{"swapon", NULL, NOSYS_KERNEL, 0}, /* 167 */
{"swapoff", NULL, NOSYS_KERNEL, 0}, /* 168 */
{"reboot", NULL, 0, 4}, /* 169 */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
index 5b46e66035..2c0d982bd6 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
@@ -111,6 +111,7 @@ extern long lx_listxattr();
extern long lx_mkdir();
extern long lx_mkdirat();
extern long lx_modify_ldt();
+extern long lx_mount();
extern long lx_nanosleep();
extern long lx_oldgetrlimit();
extern long lx_open();
@@ -171,6 +172,8 @@ extern long lx_removexattr();
extern long lx_tgkill();
extern long lx_time();
extern long lx_tkill();
+extern long lx_umount();
+extern long lx_umount2();
extern long lx_uname();
extern long lx_wait4();
extern long lx_waitid();
@@ -199,6 +202,7 @@ extern long lx_writev();
#if defined(__amd64)
#define LX_SYS_close 3
#define LX_SYS_gettimeofday 96
+#define LX_SYS_mount 165
#define LX_SYS_time 201
#define LX_SYS_io_setup 206
#define LX_SYS_clock_gettime 228
@@ -207,11 +211,13 @@ extern long lx_writev();
#define LX_SYS32_close 6
#define LX_SYS32_gettimeofday 78
#define LX_SYS32_time 13
+#define LX_SYS32_mount 21
#define LX_SYS32_clock_gettime 265
#define LX_SYS32_io_setup 245
#define LX_SYS32_getcpu 318
#elif defined(__i386)
#define LX_SYS_close 6
+#define LX_SYS_mount 21
#define LX_SYS_gettimeofday 78
#define LX_SYS_time 13
#define LX_SYS_clock_gettime 265
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_mount.c b/usr/src/uts/common/brand/lx/syscall/lx_mount.c
new file mode 100644
index 0000000000..825d70cf6a
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_mount.c
@@ -0,0 +1,650 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/ctype.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+#include <sys/types.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_autofs.h>
+
+#define tolower(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x))
+
+/*
+ * mount(2) is significantly different between Linux and illumos. One of the
+ * main differences is between the set of flags. Some flags on Linux can be
+ * translated to an illumos equivalent, some are converted to a
+ * filesystem-specific option, while others have no equivalent whatsoever.
+ *
+ * Another big difference is that mounting NFS is fully handled in the kernel on
+ * Linux whereas on illumos a lot of preliminary work is done by the NFS mount
+ * command before calling mount(2). As a simplification, we forward NFS
+ * mount calls back out to the user-level library which does the same kind of
+ * preliminary processing that is done by the native user-level NFS mount code.
+ */
+#define LX_MS_MGC_VAL 0xC0ED0000
+#define LX_MS_RDONLY 0x00000001
+#define LX_MS_NOSUID 0x00000002
+#define LX_MS_NODEV 0x00000004
+#define LX_MS_NOEXEC 0x00000008
+#define LX_MS_SYNCHRONOUS 0x00000010
+#define LX_MS_REMOUNT 0x00000020
+#define LX_MS_MANDLOCK 0x00000040
+#define LX_MS_NOATIME 0x00000400
+#define LX_MS_NODIRATIME 0x00000800
+#define LX_MS_BIND 0x00001000
+#define LX_MS_MOVE 0x00002000
+#define LX_MS_REC 0x00004000
+#define LX_MS_SILENT 0x00008000
+#define LX_MS_POSIXACL 0x00010000
+#define LX_MS_UNBINDABLE 0x00020000
+#define LX_MS_PRIVATE 0x00040000
+#define LX_MS_SLAVE 0x00080000
+#define LX_MS_SHARED 0x00100000
+#define LX_MS_RELATIME 0x00200000
+#define LX_MS_KERNMOUNT 0x00400000
+#define LX_MS_I_VERSION 0x00800000
+#define LX_MS_STRICTATIME 0x01000000
+#define LX_MS_LAZYTIME 0x02000000
+
+/* Linux kernel-internal flags - ignored if passed in */
+#define LX_MS_NOSEC 0x10000000
+#define LX_MS_BORN 0x20000000
+#define LX_MS_ACTIVE 0x40000000
+#define LX_MS_NOUSER 0x80000000
+
+#define LX_MS_SUPPORTED (LX_MS_MGC_VAL | \
+ LX_MS_RDONLY | LX_MS_NOSUID | \
+ LX_MS_NODEV | LX_MS_NOEXEC | \
+ LX_MS_REMOUNT | LX_MS_NOATIME | \
+ LX_MS_BIND | LX_MS_SILENT | \
+ LX_MS_STRICTATIME | LX_MS_NOSEC | \
+ LX_MS_BORN | LX_MS_ACTIVE | LX_MS_NOUSER)
+
+/*
+ * support definitions
+ */
+typedef enum mount_opt_type {
+ MOUNT_OPT_INVALID = 0,
+ MOUNT_OPT_NORMAL = 1, /* option value: none */
+ MOUNT_OPT_UINT = 2, /* option value: unsigned int */
+ MOUNT_OPT_PASSTHRU = 3 /* option value: validated downstream */
+} mount_opt_type_t;
+
+typedef struct mount_opt {
+ char *mo_name;
+ mount_opt_type_t mo_type;
+} mount_opt_t;
+
+/* From uts/common/syscall/umount.c */
+extern int umount2(char *, int);
+
+/*
+ * Globals
+ */
+static mount_opt_t lofs_options[] = {
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_proc_options[] = {
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_sysfs_options[] = {
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_tmpfs_options[] = {
+ { "size", MOUNT_OPT_PASSTHRU },
+ { "mode", MOUNT_OPT_UINT },
+ { "uid", MOUNT_OPT_UINT },
+ { "gid", MOUNT_OPT_UINT },
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_autofs_options[] = {
+ { LX_MNTOPT_FD, MOUNT_OPT_UINT },
+ { LX_MNTOPT_PGRP, MOUNT_OPT_UINT },
+ { LX_MNTOPT_MINPROTO, MOUNT_OPT_UINT },
+ { LX_MNTOPT_MAXPROTO, MOUNT_OPT_UINT },
+ { LX_MNTOPT_INDIRECT, MOUNT_OPT_NORMAL },
+ { LX_MNTOPT_DIRECT, MOUNT_OPT_NORMAL },
+ { LX_MNTOPT_OFFSET, MOUNT_OPT_NORMAL },
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+
+/*
+ * Check the mount options.
+ *
+ * On illumos all mount option verification is done by the user-level mount
+ * command. Invalid options are simply ignored by domount(). Thus, we check
+ * here for invalid/unsupported options.
+ */
+static int
+lx_mnt_opt_verify(char *opts, mount_opt_t *mop)
+{
+ int opts_len = strlen(opts);
+ char *opt, *tp;
+ int opt_len, i;
+ boolean_t last = B_FALSE;
+
+ ASSERT((opts != NULL) && (mop != NULL));
+
+ /* If no options were specified, nothing to do. */
+ if (opts_len == 0)
+ return (0);
+
+ /* If no options are allowed, fail. */
+ if (mop[0].mo_name == NULL)
+ return (ENOTSUP);
+
+ /* Don't accept leading or trailing ','. */
+ if ((opts[0] == ',') || (opts[opts_len] == ','))
+ return (EINVAL);
+
+ /* Don't accept sequential ','. */
+ for (i = 1; i < opts_len; i++) {
+ if ((opts[i - 1] == ',') && (opts[i] == ','))
+ return (EINVAL);
+ }
+
+ /*
+ * Verify each prop one at a time. There is no strtok in the kernel but
+ * it's easy to tokenize the entry ourselves.
+ */
+ opt = opts;
+ for (tp = opt; *tp != ',' && *tp != '\0'; tp++)
+ ;
+ if (*tp == ',') {
+ *tp = '\0';
+ } else {
+ last = B_TRUE;
+ }
+ for (;;) {
+ opt_len = strlen(opt);
+
+ /* Check for matching option/value pair. */
+ for (i = 0; mop[i].mo_name != NULL; i++) {
+ char *ovalue;
+ int ovalue_len, mo_len;
+
+ /* If the options is too short don't bother comparing */
+ mo_len = strlen(mop[i].mo_name);
+ if (opt_len < mo_len) {
+ /* Keep trying to find a match. */
+ continue;
+ }
+
+ /* Compare the option to an allowed option. */
+ if (strncmp(mop[i].mo_name, opt, mo_len) != 0) {
+ /* Keep trying to find a match. */
+ continue;
+ }
+
+ if (mop[i].mo_type == MOUNT_OPT_NORMAL) {
+ /* The option doesn't take a value. */
+ if (opt_len == mo_len) {
+ /* This option is ok. */
+ break;
+ } else {
+ /* Keep trying to find a match. */
+ continue;
+ }
+ }
+
+ /* This options takes a value. */
+ if ((opt_len == mo_len) || (opt[mo_len] != '=')) {
+ /* Keep trying to find a match. */
+ continue;
+ }
+
+ /* We have an option match. Verify option value. */
+ ovalue = &opt[mo_len] + 1;
+ ovalue_len = strlen(ovalue);
+
+ /* Value can't be zero length string. */
+ if (ovalue_len == 0) {
+ goto bad;
+ }
+
+ if (mop[i].mo_type == MOUNT_OPT_UINT) {
+ int j;
+ /* Verify that value is an unsigned int. */
+ for (j = 0; j < ovalue_len; j++) {
+ if (!ISDIGIT(ovalue[j])) {
+ goto bad;
+ }
+ }
+ } else if (mop[i].mo_type == MOUNT_OPT_PASSTHRU) {
+ /* Filesystem will do its own validation. */
+ break;
+ } else {
+ /* Unknown option type specified. */
+ goto bad;
+ }
+
+ /* The option is ok. */
+ break;
+ }
+
+ /* If there were no matches this is an unsupported option. */
+ if (mop[i].mo_name == NULL) {
+ goto bad;
+ }
+
+ /*
+ * This option is ok, either we're done or move on to the next
+ * option.
+ */
+ if (last)
+ break;
+
+ *tp = ',';
+ opt = tp + 1;
+ for (tp = opt; *tp != ',' && *tp != '\0'; tp++)
+ ;
+ if (*tp == ',') {
+ *tp = '\0';
+ } else {
+ last = B_TRUE;
+ }
+ };
+
+ /* We verified all the options. */
+ return (0);
+
+bad:
+ if (!last) {
+ *tp = ',';
+ }
+ return (EINVAL);
+}
+
+/*
+ * Remove an option from the string and save it in the provided buffer.
+ * The option string should have already been verified as valid.
+ * Return 0 if not present, -1 if error, and 1 if present and fine.
+ */
+static int
+lx_mnt_opt_rm(char *opts, char *rmopt, char *retstr, int retlen)
+{
+ int opts_len = strlen(opts);
+ char *optstart, *optend;
+ int optlen;
+
+ ASSERT((opts != NULL) && (rmopt != NULL));
+
+ retstr[0] = '\0';
+
+ /* If no options were specified, there's no problem. */
+ if (opts_len == 0)
+ return (0);
+
+ if ((optstart = strstr(opts, rmopt)) == NULL)
+ return (0);
+
+ for (optend = optstart; *optend != ',' && *optend != '\0'; optend++)
+ ;
+
+ /*LINTED*/
+ optlen = optend - optstart;
+ if (optlen >= retlen)
+ return (-1);
+ (void) strncpy(retstr, optstart, optlen);
+ retstr[optlen] = '\0';
+
+ if (*optend == ',')
+ optend++;
+
+ optlen = strlen(optend) + 1;
+ bcopy(optend, optstart, optlen);
+
+ if (*optstart == '\0' && optstart != opts) {
+ /* removed last opt and it had a preceeding opt, remove comma */
+ *(optstart - 1) = '\0';
+ }
+
+ return (1);
+}
+
+static int
+lx_mnt_opt_val(char *opt, int *valp)
+{
+ char *op, *ep;
+ long lval;
+
+ if ((op = strchr(opt, '=')) == NULL)
+ return (-1);
+
+ op++;
+ if (!ISDIGIT(*op))
+ return (-1);
+
+ if (ddi_strtoul(op, &ep, 10, (ulong_t *)&lval) != 0 || lval > INT_MAX) {
+ return (-1);
+ }
+
+ if (*ep != '\0')
+ return (-1);
+
+ *valp = (int)lval;
+ return (0);
+}
+
+static int
+lx_mnt_add_opt(char *option, char *buf, size_t buf_size)
+{
+ char *fmt_str = NULL;
+ size_t len;
+
+ ASSERT((option != NULL) && (strlen(option) > 0));
+ ASSERT((buf != NULL) && (buf_size > 0));
+
+ if (buf[0] == '\0') {
+ fmt_str = "%s";
+ } else {
+ fmt_str = ",%s";
+ }
+
+ len = strlen(buf);
+ VERIFY(len <= buf_size);
+ buf_size -= len;
+ buf += len;
+
+ if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1))
+ return (EOVERFLOW);
+ return (0);
+}
+
+static int
+lx_mnt_copyin_arg(const char *from, char *to, size_t len)
+{
+ size_t slen;
+ int rv;
+
+ rv = copyinstr(from, to, len, &slen);
+ if (rv == ENAMETOOLONG || slen == len)
+ return (ENAMETOOLONG);
+ if (rv != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+long
+lx_mount(const char *sourcep, const char *targetp, const char *fstypep,
+ uint_t flags, const void *datap)
+{
+ char fstype[16];
+ char source[MAXPATHLEN];
+ char target[MAXPATHLEN];
+ char options[MAX_MNTOPT_STR];
+ int sflags, rv;
+ struct mounta ma, *map = &ma;
+ vfs_t *vfsp;
+ vnode_t *vp = NULL;
+ int uid = -1;
+ int gid = -1;
+
+ if ((rv = lx_mnt_copyin_arg(fstypep, fstype, sizeof (fstype))) != 0) {
+ if (rv == ENAMETOOLONG)
+ return (set_errno(ENODEV));
+ return (set_errno(rv));
+ }
+
+ /*
+ * Vector back out to userland emulation for NFS.
+ */
+ if (strcmp(fstype, "nfs") == 0) {
+ uintptr_t uargs[5] = {(uintptr_t)sourcep, (uintptr_t)targetp,
+ (uintptr_t)fstypep, (uintptr_t)flags, (uintptr_t)datap};
+
+ /* The userspace emulation will do the lx_syscall_return() */
+ ttolxlwp(curthread)->br_eosys = JUSTRETURN;
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_emulate_user32(ttolwp(curthread), LX_SYS32_mount,
+ uargs);
+ } else
+#endif
+ {
+ lx_emulate_user(ttolwp(curthread), LX_SYS_mount, uargs);
+ }
+ return (0);
+ }
+
+ sflags = MS_SYSSPACE | MS_OPTIONSTR;
+ options[0] = '\0';
+
+ /* Copy in parameters that are always present. */
+ if ((rv = lx_mnt_copyin_arg(sourcep, source, sizeof (source))) != 0)
+ return (set_errno(rv));
+
+ if ((rv = lx_mnt_copyin_arg(targetp, target, sizeof (target))) != 0)
+ return (set_errno(rv));
+
+ /*
+ * While SunOS is picky about mount(2) target paths being absolute,
+ * Linux is not so strict. In order to facilitate this looser
+ * requirement we must lookup the full path.
+ */
+ if (target[0] != '/') {
+ vnode_t *vp;
+
+ if ((rv = lookupnameatcred(target, UIO_SYSSPACE, FOLLOW,
+ NULLVPP, &vp, NULL, CRED())) != 0)
+ return (set_errno(rv));
+
+ rv = vnodetopath(NULL, vp, target, MAXPATHLEN, CRED());
+ VN_RELE(vp);
+ if (rv != 0)
+ return (set_errno(rv));
+ }
+
+ /* Make sure we support the requested mount flags. */
+ if ((flags & ~LX_MS_SUPPORTED) != 0)
+ return (set_errno(ENOTSUP));
+
+ /* Copy in Linux mount options. */
+ if (datap != NULL &&
+ (rv = lx_mnt_copyin_arg(datap, options, sizeof (options))) != 0)
+ return (set_errno(rv));
+
+ /* Do filesystem specific mount work. */
+ if (flags & LX_MS_BIND) {
+ /* If MS_BIND is set, we turn this into a lofs mount. */
+ (void) strcpy(fstype, "lofs");
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lofs_options)) != 0)
+ return (set_errno(rv));
+ } else if (strcmp(fstype, "tmpfs") == 0) {
+ char idstr[64];
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_tmpfs_options)) != 0)
+ return (set_errno(rv));
+
+ /*
+ * Linux defaults to mode=1777 for tmpfs mounts.
+ */
+ if (strstr(options, "mode=") == NULL) {
+ if (options[0] != '\0')
+ (void) strlcat(options, ",", sizeof (options));
+ (void) strlcat(options, "mode=1777", sizeof (options));
+ }
+
+ switch (lx_mnt_opt_rm(options, "uid=", idstr, sizeof (idstr))) {
+ case 0:
+ uid = -1;
+ break;
+ case 1:
+ if (lx_mnt_opt_val(idstr, &uid) < 0)
+ return (set_errno(EINVAL));
+ break;
+ default:
+ return (set_errno(E2BIG));
+ }
+ switch (lx_mnt_opt_rm(options, "gid=", idstr, sizeof (idstr))) {
+ case 0:
+ gid = -1;
+ break;
+ case 1:
+ if (lx_mnt_opt_val(idstr, &gid) < 0)
+ return (set_errno(EINVAL));
+ break;
+ default:
+ return (set_errno(E2BIG));
+ }
+
+ /*
+ * Linux seems to always allow overlay mounts. We allow this
+ * everywhere except under /dev where it interferes with device
+ * emulation.
+ */
+ if (strcmp(targetp, "/dev") != 0 &&
+ strncmp(targetp, "/dev/", 5) != 0)
+ sflags |= MS_OVERLAY;
+ } else if (strcmp(fstype, "proc") == 0) {
+ /* Translate proc mount requests to lx_proc requests. */
+ (void) strcpy(fstype, "lx_proc");
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_proc_options)) != 0)
+ return (set_errno(rv));
+ } else if (strcmp(fstype, "sysfs") == 0) {
+ /* Translate sysfs mount requests to lx_sysfs requests. */
+ (void) strcpy(fstype, "lx_sysfs");
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_sysfs_options)) != 0)
+ return (set_errno(rv));
+ } else if (strcmp(fstype, "cgroup") == 0) {
+ /* Translate cgroup mount requests to lx_cgroup requests. */
+ (void) strcpy(fstype, "lx_cgroup");
+
+ /*
+ * Currently don't verify Linux mount options since we can
+ * have a subsystem string provided.
+ */
+ } else if (strcmp(fstype, "autofs") == 0) {
+ /* Translate autofs mount requests to lxautofs requests. */
+ (void) strcpy(fstype, LX_AUTOFS_NAME);
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_autofs_options)) != 0)
+ return (set_errno(rv));
+
+ /* Linux seems to always allow overlay mounts */
+ sflags |= MS_OVERLAY;
+ } else {
+ return (set_errno(ENODEV));
+ }
+
+ /* Convert some Linux flags to illumos flags. */
+ if (flags & LX_MS_RDONLY)
+ sflags |= MS_RDONLY;
+ if (flags & LX_MS_NOSUID)
+ sflags |= MS_NOSUID;
+ if (flags & LX_MS_REMOUNT)
+ sflags |= MS_REMOUNT;
+
+ /*
+ * Convert some Linux flags to illumos option strings.
+ */
+ if (flags & LX_MS_STRICTATIME) {
+ /*
+ * The "strictatime" mount option ensures that none of the
+ * weaker atime-related mode options are in effect.
+ */
+ flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME);
+ }
+ if ((flags & LX_MS_NODEV) &&
+ (rv = lx_mnt_add_opt("nodev", options, sizeof (options))) != 0)
+ return (set_errno(rv));
+ if ((flags & LX_MS_NOEXEC) &&
+ (rv = lx_mnt_add_opt("noexec", options, sizeof (options))) != 0)
+ return (set_errno(rv));
+ if ((flags & LX_MS_NOATIME) &&
+ (rv = lx_mnt_add_opt("noatime", options, sizeof (options))) != 0)
+ return (set_errno(rv));
+
+ if ((rv = lookupname(target, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) != 0)
+ return (set_errno(rv));
+
+ /* If mounting proc over itself, just return ok */
+ if (strcmp(fstype, "lx_proc") == 0 && strcmp("lx_proc",
+ vfssw[vp->v_vfsp->vfs_fstype].vsw_name) == 0) {
+ VN_RELE(vp);
+ return (0);
+ }
+
+ map->spec = source;
+ map->dir = target;
+ map->flags = sflags;
+ map->fstype = fstype;
+ map->dataptr = NULL;
+ map->datalen = 0;
+ map->optptr = options;
+ map->optlen = sizeof (options);
+
+ rv = domount(NULL, map, vp, CRED(), &vfsp);
+ VN_RELE(vp);
+ if (rv != 0)
+ return (set_errno(rv));
+
+ VFS_RELE(vfsp);
+ if (strcmp(fstype, "tmpfs") == 0 && (uid != -1 || gid != -1)) {
+ /* Handle tmpfs uid/gid mount options. */
+ (void) lx_chown(target, uid, gid);
+ }
+
+ return (0);
+}
+
+/*
+ * umount() is identical to illumos, though implemented on top of umount2().
+ */
+long
+lx_umount(char *path)
+{
+ return (umount2(path, 0));
+}
+
+/*
+ * The Linux umount2() system call is identical to illumos but has a different
+ * value for MNT_FORCE (the logical equivalent to MS_FORCE).
+ */
+#define LX_MNT_FORCE 0x1
+
+long
+lx_umount2(char *path, int flg)
+{
+ int flags = 0;
+
+ if (flg & ~LX_MNT_FORCE)
+ return (set_errno(EINVAL));
+
+ if (flg & LX_MNT_FORCE)
+ flags |= MS_FORCE;
+
+ return (umount2(path, flags));
+}
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index d279def122..3f8865ff1c 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -335,6 +335,7 @@ LX_BRAND_OBJS = \
lx_misc.o \
lx_mkdir.o \
lx_modify_ldt.o \
+ lx_mount.o \
lx_open.o \
lx_personality.o \
lx_pid.o \