1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <alloca.h>
  30 #include <assert.h>
  31 #include <ctype.h>
  32 #include <fcntl.h>
  33 #include <errno.h>
  34 #include <signal.h>
  35 #include <string.h>
  36 #include <strings.h>
  37 #include <nfs/mount.h>
  38 #include <sys/types.h>
  39 #include <sys/mount.h>
  40 #include <sys/param.h>
  41 #include <sys/stat.h>
  42 #include <sys/types.h>
  43 #include <unistd.h>
  44 
  45 #include <sys/lx_autofs.h>
  46 #include <sys/lx_debug.h>
  47 #include <sys/lx_misc.h>
  48 #include <sys/lx_mount.h>
  49 
  50 /*
  51  * support definitions
  52  */
  53 union fh_buffer {
  54         struct nfs_fid  fh2;
  55         struct nfs_fh3  fh3;
  56         char            fh_data[NFS3_FHSIZE + 2];
  57 };
  58 
  59 typedef enum mount_opt_type {
  60         MOUNT_OPT_INVALID       = 0,
  61         MOUNT_OPT_NORMAL        = 1,    /* option value: none */
  62         MOUNT_OPT_UINT          = 2     /* option value: unsigned int */
  63 } mount_opt_type_t;
  64 
  65 typedef struct mount_opt {
  66         char                    *mo_name;
  67         mount_opt_type_t        mo_type;
  68 } mount_opt_t;
  69 
  70 
  71 /*
  72  * Globals
  73  */
  74 mount_opt_t lofs_options[] = {
  75         { NULL, MOUNT_OPT_INVALID }
  76 };
  77 
  78 mount_opt_t lx_proc_options[] = {
  79         { NULL, MOUNT_OPT_INVALID }
  80 };
  81 
  82 mount_opt_t lx_autofs_options[] = {
  83         { LX_MNTOPT_FD,         MOUNT_OPT_UINT },
  84         { LX_MNTOPT_PGRP,       MOUNT_OPT_UINT },
  85         { LX_MNTOPT_MINPROTO,   MOUNT_OPT_UINT },
  86         { LX_MNTOPT_MAXPROTO,   MOUNT_OPT_UINT },
  87 };
  88 
  89 
  90 /*
  91  * i_lx_opt_verify() - Check the mount options.
  92  *
  93  * You might wonder why we're being so strict about the mount options
  94  * we allow.  The reason is that normally all mount option verification
  95  * is done by the Solaris userland mount command.  Once mount options
  96  * are passed to the kernel, invalid options are simply ignored.  So
  97  * if we actually want to catch requests for functionality that we
  98  * don't support, or if we want to make sure that we don't randomly
  99  * enable options that we haven't check to make sure they have the
 100  * same syntax on Linux and Solaris, we need to reject any options
 101  * we don't know to be ok here.
 102  */
 103 static int
 104 i_lx_opt_verify(char *opts, mount_opt_t *mop)
 105 {
 106         int     opts_len = strlen(opts);
 107         char    *opts_tmp, *opt;
 108         int     opt_len, i;
 109 
 110         assert((opts != NULL) && (mop != NULL));
 111 
 112         /* If no options were specified, there's no problem. */
 113         if (opts_len == 0)
 114                 return (1);
 115 
 116         /* If no options are allowed, fail. */
 117         if (mop[0].mo_name == NULL)
 118                 return (0);
 119 
 120         /* Don't accept leading or trailing ','. */
 121         if ((opts[0] == ',') || (opts[opts_len] == ','))
 122                 return (0);
 123 
 124         /* Don't accept sequential ','. */
 125         for (i = 1; i < opts_len; i++)
 126                 if ((opts[i - 1] ==  ',') && (opts[i] ==  ','))
 127                         return (0);
 128 
 129         /*
 130          * We're going to use strtok() which modifies the target
 131          * string so make a temporary copy.
 132          */
 133         opts_tmp = SAFE_ALLOCA(opts_len);
 134         if (opts_tmp == NULL)
 135                 return (-1);
 136         bcopy(opts, opts_tmp, opts_len + 1);
 137 
 138         /* Verify each prop one at a time. */
 139         opt = strtok(opts_tmp, ",");
 140         opt_len = strlen(opt);
 141         for (;;) {
 142 
 143                 /* Check for matching option/value pair. */
 144                 for (i = 0; mop[i].mo_name != NULL; i++) {
 145                         char    *ovalue;
 146                         int     ovalue_len, mo_len;
 147 
 148                         /* If the options is too short don't bother comparing */
 149                         mo_len = strlen(mop[i].mo_name);
 150                         if (opt_len < mo_len) {
 151                                 /* Keep trying to find a match. */
 152                                 continue;
 153                         }
 154 
 155                         /* Compare the option to an allowed option. */
 156                         if (strncmp(mop[i].mo_name, opt, mo_len) != 0) {
 157                                 /* Keep trying to find a match. */
 158                                 continue;
 159                         }
 160 
 161                         if (mop[i].mo_type == MOUNT_OPT_NORMAL) {
 162                                 /* The option doesn't take a value. */
 163                                 if (opt_len == mo_len) {
 164                                         /* This option is ok. */
 165                                         break;
 166                                 } else {
 167                                         /* Keep trying to find a match. */
 168                                         continue;
 169                                 }
 170                         }
 171 
 172                         /* This options takes a value. */
 173                         if ((opt_len == mo_len) || (opt[mo_len] != '=')) {
 174                                 /* Keep trying to find a match. */
 175                                 continue;
 176                         }
 177 
 178                         /* We have an option match.  Verify option value. */
 179                         ovalue = &opt[mo_len] + 1;
 180                         ovalue_len = strlen(ovalue);
 181 
 182                         /* Value can't be zero length string. */
 183                         if (ovalue_len == 0)
 184                                 return (0);
 185 
 186                         if (mop[i].mo_type == MOUNT_OPT_UINT) {
 187                                 int j;
 188                                 /* Verify that value is an unsigned int. */
 189                                 for (j = 0; j < ovalue_len; j++)
 190                                         if (!isdigit(ovalue[j]))
 191                                                 return (0);
 192                         } else {
 193                                 /* Unknown option type specified. */
 194                                 assert(0);
 195                         }
 196 
 197                         /* The option is ok. */
 198                         break;
 199                 }
 200 
 201                 /* If there were no matches this is an unsupported option. */
 202                 if (mop[i].mo_name == NULL)
 203                         return (0);
 204 
 205                 /* This option is ok, move onto the next option. */
 206                 if ((opt = strtok(NULL, ",")) == NULL)
 207                         break;
 208                 opt_len = strlen(opt);
 209         };
 210 
 211         /* We verified all the options. */
 212         return (1);
 213 }
 214 
 215 static int
 216 i_add_option(char *option, char *buf, size_t buf_size)
 217 {
 218         char *fmt_str = NULL;
 219 
 220         assert((option != NULL) && (strlen(option) > 0));
 221         assert((buf != NULL) && (buf_size > 0));
 222 
 223         if (buf[0] == '\0') {
 224                 fmt_str = "%s";
 225         } else {
 226                 fmt_str = ",%s";
 227         }
 228 
 229         buf_size -= strlen(buf);
 230         buf += strlen(buf);
 231 
 232         /*LINTED*/
 233         if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1))
 234                 return (-EOVERFLOW);
 235         return (0);
 236 }
 237 
 238 static int
 239 i_add_option_int(char *option, int val, char *buf, size_t buf_size)
 240 {
 241         char *fmt_str = NULL;
 242 
 243         assert((option != NULL) && (strlen(option) > 0));
 244         assert((buf != NULL) && (buf_size > 0));
 245 
 246         if (buf[0] == '\0') {
 247                 fmt_str = "%s=%d";
 248         } else {
 249                 fmt_str = ",%s=%d";
 250         }
 251 
 252         buf_size -= strlen(buf);
 253         buf += strlen(buf);
 254 
 255         /*LINTED*/
 256         if (snprintf(buf, buf_size, fmt_str, option, val) > (buf_size - 1))
 257                 return (-EOVERFLOW);
 258         return (0);
 259 }
 260 
 261 static int
 262 i_make_nfs_args(lx_nfs_mount_data_t *lx_nmd, struct nfs_args *nfs_args,
 263     struct netbuf *nfs_args_addr, struct knetconfig *nfs_args_knconf,
 264     union fh_buffer *nfs_args_fh, struct sec_data *nfs_args_secdata,
 265     char *fstype, char *options, int options_size)
 266 {
 267         struct stat     statbuf;
 268         int             i, rv, use_tcp;
 269 
 270         /* Sanity check the incomming Linux request. */
 271         if ((lx_nmd->nmd_rsize < 0) || (lx_nmd->nmd_wsize < 0) ||
 272             (lx_nmd->nmd_timeo < 0) || (lx_nmd->nmd_retrans < 0) ||
 273             (lx_nmd->nmd_acregmin < 0) || (lx_nmd->nmd_acregmax < 0) ||
 274             (lx_nmd->nmd_acdirmax < 0)) {
 275                 return (-EINVAL);
 276         }
 277 
 278         /*
 279          * Additional sanity checks of incomming request.
 280          *
 281          * Some of the sanity checks below should probably return
 282          * EINVAL (or some other error code) instead or ENOTSUP,
 283          * but without experiminting on Linux to see how it
 284          * deals with certain strange values there is no way
 285          * to really know what we should return, hence we return
 286          * ENOTSUP to tell us that eventually if we see some
 287          * application hitting the problem we can go to a real
 288          * Linux system, figure out how it deals with the situation
 289          * and update our code to handle it in the same fashion.
 290          */
 291         if (lx_nmd->nmd_version != 4) {
 292                 lx_unsupported("unsupported nfs mount request, "
 293                     "unrecognized NFS mount structure: %d\n",
 294                     lx_nmd->nmd_version);
 295                 return (-ENOTSUP);
 296         }
 297         if ((lx_nmd->nmd_flags & ~LX_NFS_MOUNT_SUPPORTED) != 0) {
 298                 lx_unsupported("unsupported nfs mount request, "
 299                     "flags: 0x%x\n", lx_nmd->nmd_flags);
 300                 return (-ENOTSUP);
 301         }
 302         if (lx_nmd->nmd_addr.sin_family != AF_INET) {
 303                 lx_unsupported("unsupported nfs mount request, "
 304                     "transport address family: 0x%x\n",
 305                     lx_nmd->nmd_addr.sin_family);
 306                 return (-ENOTSUP);
 307         }
 308         for (i = 0; i < LX_NMD_MAXHOSTNAMELEN; i++) {
 309                 if (lx_nmd->nmd_hostname[i] == '\0')
 310                         break;
 311         }
 312         if (i == 0) {
 313                 lx_unsupported("unsupported nfs mount request, "
 314                     "no hostname specified\n");
 315                 return (-ENOTSUP);
 316         }
 317         if (i == LX_NMD_MAXHOSTNAMELEN) {
 318                 lx_unsupported("unsupported nfs mount request, "
 319                     "hostname not terminated\n");
 320                 return (-ENOTSUP);
 321         }
 322         if (lx_nmd->nmd_namlen < i) {
 323                 lx_unsupported("unsupported nfs mount request, "
 324                     "invalid namlen value: 0x%x\n", lx_nmd->nmd_namlen);
 325                 return (-ENOTSUP);
 326         }
 327         if (lx_nmd->nmd_bsize != 0) {
 328                 lx_unsupported("unsupported nfs mount request, "
 329                     "bsize value: 0x%x\n", lx_nmd->nmd_bsize);
 330                 return (-ENOTSUP);
 331         }
 332 
 333         /* Initialize and clear the output structure pointers passed in. */
 334         bzero(nfs_args, sizeof (*nfs_args));
 335         bzero(nfs_args_addr, sizeof (*nfs_args_addr));
 336         bzero(nfs_args_knconf, sizeof (*nfs_args_knconf));
 337         bzero(nfs_args_fh, sizeof (*nfs_args_fh));
 338         bzero(nfs_args_secdata, sizeof (*nfs_args_secdata));
 339         nfs_args->addr = nfs_args_addr;
 340         nfs_args->knconf = nfs_args_knconf;
 341         nfs_args->fh = (caddr_t)nfs_args_fh;
 342         nfs_args->nfs_ext_u.nfs_extB.secdata = nfs_args_secdata;
 343 
 344         /* Check if we're using tcp. */
 345         use_tcp = (lx_nmd->nmd_flags & LX_NFS_MOUNT_TCP) ? 1 : 0;
 346 
 347         /*
 348          * These seem to be the default flags used by Solaris for v2 and v3
 349          * nfs mounts.
 350          *
 351          * Don't bother with NFSMNT_TRYRDMA since we always specify a
 352          * transport (either udp or tcp).
 353          */
 354         nfs_args->flags = NFSMNT_NEWARGS | NFSMNT_KNCONF | NFSMNT_INT |
 355             NFSMNT_HOSTNAME;
 356 
 357         /* Translate some Linux mount flags into Solaris mount flags. */
 358         if (lx_nmd->nmd_flags & LX_NFS_MOUNT_SOFT)
 359                 nfs_args->flags |= NFSMNT_SOFT;
 360         if (lx_nmd->nmd_flags & LX_NFS_MOUNT_INTR)
 361                 nfs_args->flags |= NFSMNT_INT;
 362         if (lx_nmd->nmd_flags & LX_NFS_MOUNT_POSIX)
 363                 nfs_args->flags |= NFSMNT_POSIX;
 364         if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NOCTO)
 365                 nfs_args->flags |= NFSMNT_NOCTO;
 366         if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NOAC)
 367                 nfs_args->flags |= NFSMNT_NOAC;
 368         if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NONLM)
 369                 nfs_args->flags |= NFSMNT_LLOCK;
 370 
 371         if ((lx_nmd->nmd_flags & LX_NFS_MOUNT_VER3) != 0) {
 372                 (void) strcpy(fstype, "nfs3");
 373                 if ((rv = i_add_option_int("vers", 3,
 374                             options, options_size)) != 0)
 375                         return (rv);
 376 
 377                 if (lx_nmd->nmd_root.lx_fh3_length >
 378                     sizeof (nfs_args_fh->fh3.fh3_u.data)) {
 379                         lx_unsupported("unsupported nfs mount request, "
 380                             "nfs file handle length: 0x%x\n",
 381                             lx_nmd->nmd_root.lx_fh3_length);
 382                         return (-ENOTSUP);
 383                 }
 384 
 385                 /* Set the v3 file handle info. */
 386                 nfs_args_fh->fh3.fh3_length = lx_nmd->nmd_root.lx_fh3_length;
 387                 bcopy(&lx_nmd->nmd_root.lx_fh3_data,
 388                     nfs_args_fh->fh3.fh3_u.data,
 389                     lx_nmd->nmd_root.lx_fh3_length);
 390         } else {
 391                 /*
 392                  * Assume nfs v2.  Note that this could also be a v1
 393                  * mount request but there doesn't seem to be any difference
 394                  * in the parameters passed to the Linux mount system
 395                  * call for v1 or v2 mounts so there is no way of really
 396                  * knowing.
 397                  */
 398                 (void) strcpy(fstype, "nfs");
 399                 if ((rv = i_add_option_int("vers", 2,
 400                             options, options_size)) != 0)
 401                         return (rv);
 402 
 403                 /* Solaris seems to add this flag when using v2. */
 404                 nfs_args->flags |= NFSMNT_SECDEFAULT;
 405 
 406                 /* Set the v2 file handle info. */
 407                 bcopy(&lx_nmd->nmd_old_root,
 408                     nfs_args_fh, sizeof (nfs_args_fh->fh2));
 409         }
 410 
 411         /*
 412          * We can't use getnetconfig() here because there is no netconfig
 413          * database in linux.
 414          */
 415         nfs_args_knconf->knc_protofmly = "inet";
 416         if (use_tcp) {
 417                 /*
 418                  * TCP uses NC_TPI_COTS_ORD semantics.
 419                  * See /etc/netconfig.
 420                  */
 421                 nfs_args_knconf->knc_semantics = NC_TPI_COTS_ORD;
 422                 nfs_args_knconf->knc_proto = "tcp";
 423                 if ((rv = i_add_option("proto=tcp",
 424                             options, options_size)) != 0)
 425                         return (rv);
 426                 if (stat("/dev/tcp", &statbuf) != 0)
 427                         return (-errno);
 428                 nfs_args_knconf->knc_rdev = statbuf.st_rdev;
 429         } else {
 430                 /*
 431                  * Assume UDP.  UDP uses NC_TPI_CLTS semantics.
 432                  * See /etc/netconfig.
 433                  */
 434                 nfs_args_knconf->knc_semantics = NC_TPI_CLTS;
 435                 nfs_args_knconf->knc_proto = "udp";
 436                 if ((rv = i_add_option("proto=udp",
 437                             options, options_size)) != 0)
 438                         return (rv);
 439                 if (stat("/dev/udp", &statbuf) != 0)
 440                         return (-errno);
 441                 nfs_args_knconf->knc_rdev = statbuf.st_rdev;
 442         }
 443 
 444         /* Set the server address. */
 445         nfs_args_addr->maxlen = nfs_args_addr->len =
 446             sizeof (struct sockaddr_in);
 447         nfs_args_addr->buf = (char *)&lx_nmd->nmd_addr;
 448 
 449         /* Set the server hostname string. */
 450         nfs_args->hostname = lx_nmd->nmd_hostname;
 451 
 452         /* Translate Linux nfs mount parameters into Solaris mount options. */
 453         if (lx_nmd->nmd_rsize != LX_NMD_DEFAULT_RSIZE) {
 454                 if ((rv = i_add_option_int("rsize", lx_nmd->nmd_rsize,
 455                             options, options_size)) != 0)
 456                         return (rv);
 457                 nfs_args->rsize = lx_nmd->nmd_rsize;
 458                 nfs_args->flags |= NFSMNT_RSIZE;
 459         }
 460         if (lx_nmd->nmd_wsize != LX_NMD_DEFAULT_WSIZE) {
 461                 if ((rv = i_add_option_int("wsize", lx_nmd->nmd_wsize,
 462                             options, options_size)) != 0)
 463                         return (rv);
 464                 nfs_args->wsize = lx_nmd->nmd_wsize;
 465                 nfs_args->flags |= NFSMNT_WSIZE;
 466         }
 467         if ((rv = i_add_option_int("timeo", lx_nmd->nmd_timeo,
 468                     options, options_size)) != 0)
 469                 return (rv);
 470         nfs_args->timeo = lx_nmd->nmd_timeo;
 471         nfs_args->flags |= NFSMNT_TIMEO;
 472         if ((rv = i_add_option_int("retrans", lx_nmd->nmd_retrans,
 473                     options, options_size)) != 0)
 474                 return (rv);
 475         nfs_args->retrans = lx_nmd->nmd_retrans;
 476         nfs_args->flags |= NFSMNT_RETRANS;
 477         if ((rv = i_add_option_int("acregmin", lx_nmd->nmd_acregmin,
 478                     options, options_size)) != 0)
 479                 return (rv);
 480         nfs_args->acregmin = lx_nmd->nmd_acregmin;
 481         nfs_args->flags |= NFSMNT_ACREGMIN;
 482         if ((rv = i_add_option_int("acregmax", lx_nmd->nmd_acregmax,
 483                     options, options_size)) != 0)
 484                 return (rv);
 485         nfs_args->acregmax = lx_nmd->nmd_acregmax;
 486         nfs_args->flags |= NFSMNT_ACREGMAX;
 487         if ((rv = i_add_option_int("acdirmin", lx_nmd->nmd_acdirmin,
 488                     options, options_size)) != 0)
 489                 return (rv);
 490         nfs_args->acdirmin = lx_nmd->nmd_acdirmin;
 491         nfs_args->flags |= NFSMNT_ACDIRMIN;
 492         if ((rv = i_add_option_int("acdirmax", lx_nmd->nmd_acdirmax,
 493                     options, options_size)) != 0)
 494                 return (rv);
 495         nfs_args->acdirmax = lx_nmd->nmd_acdirmax;
 496         nfs_args->flags |= NFSMNT_ACDIRMAX;
 497 
 498         /* We only support nfs with a security type of AUTH_SYS. */
 499         nfs_args->nfs_args_ext = NFS_ARGS_EXTB;
 500         nfs_args_secdata->secmod = AUTH_SYS;
 501         nfs_args_secdata->rpcflavor = AUTH_SYS;
 502         nfs_args_secdata->flags = 0;
 503         nfs_args_secdata->uid = 0;
 504         nfs_args_secdata->data = NULL;
 505         nfs_args->nfs_ext_u.nfs_extB.next = NULL;
 506 
 507         /*
 508          * The Linux nfs mount command seems to pass an open socket fd
 509          * to the kernel during the mount system call.  We don't need
 510          * this fd on Solaris so just close it.
 511          */
 512         (void) close(lx_nmd->nmd_fd);
 513 
 514         return (0);
 515 }
 516 
 517 int
 518 lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
 519     uintptr_t p5)
 520 {
 521         /* Linux input arguments. */
 522         const char              *sourcep = (const char *)p1;
 523         const char              *targetp = (const char *)p2;
 524         const char              *fstypep = (const char *)p3;
 525         unsigned int            flags = (unsigned int)p4;
 526         const void              *datap = (const void *)p5;
 527 
 528         /* Variables needed for all mounts. */
 529         char                    source[MAXPATHLEN], target[MAXPATHLEN];
 530         char                    fstype[MAXPATHLEN], options[MAXPATHLEN];
 531         int                     sflags, rv;
 532 
 533         /* Variables needed for nfs mounts. */
 534         lx_nfs_mount_data_t     lx_nmd;
 535         struct nfs_args         nfs_args;
 536         struct netbuf           nfs_args_addr;
 537         struct knetconfig       nfs_args_knconf;
 538         union fh_buffer         nfs_args_fh;
 539         struct sec_data         nfs_args_secdata;
 540         char                    *sdataptr = NULL;
 541         int                     sdatalen = 0;
 542 
 543         /* Initialize Solaris mount arguments. */
 544         sflags = MS_OPTIONSTR;
 545         options[0] = '\0';
 546         sdatalen = 0;
 547 
 548         /* Copy in parameters that are always present. */
 549         rv = uucopystr((void *)sourcep, &source, sizeof (source));
 550         if ((rv == -1) || (rv == sizeof (source)))
 551                 return (-EFAULT);
 552 
 553         rv = uucopystr((void *)targetp, &target, sizeof (target));
 554         if ((rv == -1) || (rv == sizeof (target)))
 555                 return (-EFAULT);
 556 
 557         rv = uucopystr((void *)fstypep, &fstype, sizeof (fstype));
 558         if ((rv == -1) || (rv == sizeof (fstype)))
 559                 return (-EFAULT);
 560 
 561         lx_debug("\tlinux mount source: %s", source);
 562         lx_debug("\tlinux mount target: %s", target);
 563         lx_debug("\tlinux mount fstype: %s", fstype);
 564 
 565         /* Make sure we support the requested mount flags. */
 566         if ((flags & ~LX_MS_SUPPORTED) != 0) {
 567                 lx_unsupported(
 568                     "unsupported mount flags: 0x%x", flags);
 569                 return (-ENOTSUP);
 570         }
 571 
 572         /* Do filesystem specific mount work. */
 573         if (flags & LX_MS_BIND) {
 574 
 575                 /* If MS_BIND is set, we turn this into a lofs mount.  */
 576                 (void) strcpy(fstype, "lofs");
 577 
 578                 /* Copy in Linux mount options. */
 579                 if (datap != NULL) {
 580                         rv = uucopystr((void *)datap,
 581                             options, sizeof (options));
 582                         if ((rv == -1) || (rv == sizeof (options)))
 583                                 return (-EFAULT);
 584                 }
 585                 lx_debug("\tlinux mount options: \"%s\"", options);
 586 
 587                 /* Verify Linux mount options. */
 588                 if (i_lx_opt_verify(options, lofs_options) == 0) {
 589                         lx_unsupported("unsupported lofs mount options");
 590                         return (-ENOTSUP);
 591                 }
 592         } else if (strcmp(fstype, "proc") == 0) {
 593 
 594                 /* Translate proc mount requests to lx_proc requests. */
 595                 (void) strcpy(fstype, "lx_proc");
 596 
 597                 /* Copy in Linux mount options. */
 598                 if (datap != NULL) {
 599                         rv = uucopystr((void *)datap,
 600                             options, sizeof (options));
 601                         if ((rv == -1) || (rv == sizeof (options)))
 602                                 return (-EFAULT);
 603                 }
 604                 lx_debug("\tlinux mount options: \"%s\"", options);
 605 
 606                 /* Verify Linux mount options. */
 607                 if (i_lx_opt_verify(options, lx_proc_options) == 0) {
 608                         lx_unsupported("unsupported lx_proc mount options");
 609                         return (-ENOTSUP);
 610                 }
 611         } else if (strcmp(fstype, "autofs") == 0) {
 612 
 613                 /* Translate proc mount requests to lx_afs requests. */
 614                 (void) strcpy(fstype, LX_AUTOFS_NAME);
 615 
 616                 /* Copy in Linux mount options. */
 617                 if (datap != NULL) {
 618                         rv = uucopystr((void *)datap,
 619                             options, sizeof (options));
 620                         if ((rv == -1) || (rv == sizeof (options)))
 621                                 return (-EFAULT);
 622                 }
 623                 lx_debug("\tlinux mount options: \"%s\"", options);
 624 
 625                 /* Verify Linux mount options. */
 626                 if (i_lx_opt_verify(options, lx_autofs_options) == 0) {
 627                         lx_unsupported("unsupported lx_autofs mount options");
 628                         return (-ENOTSUP);
 629                 }
 630         } else if (strcmp(fstype, "nfs") == 0) {
 631 
 632                 /*
 633                  * Copy in Linux mount options.  Note that for Linux
 634                  * nfs mounts the mount options pointer (which normally
 635                  * points to a string) points to a structure.
 636                  */
 637                 if (uucopy((void *)datap, &lx_nmd, sizeof (lx_nmd)) < 0)
 638                         return (-errno);
 639 
 640                 /*
 641                  * For Solaris nfs mounts, the kernel expects a special
 642                  * strucutre, but a pointer to this structure is passed
 643                  * in via an extra parameter (sdataptr below.)
 644                  */
 645                 if ((rv = i_make_nfs_args(&lx_nmd, &nfs_args,
 646                     &nfs_args_addr, &nfs_args_knconf, &nfs_args_fh,
 647                     &nfs_args_secdata, fstype,
 648                     options, sizeof (options))) != 0)
 649                         return (rv);
 650 
 651                 /*
 652                  * For nfs mounts we need to tell the mount system call
 653                  * to expect extra parameters.
 654                  */
 655                 sflags |= MS_DATA;
 656                 sdataptr = (char *)&nfs_args;
 657                 sdatalen = sizeof (nfs_args);
 658         } else {
 659                 lx_unsupported(
 660                     "unsupported mount filesystem type: %s", fstype);
 661                 return (-ENOTSUP);
 662         }
 663 
 664         /* Convert some Linux flags to Solaris flags. */
 665         if (flags & LX_MS_RDONLY)
 666                 sflags |= MS_RDONLY;
 667         if (flags & LX_MS_NOSUID)
 668                 sflags |= MS_NOSUID;
 669         if (flags & LX_MS_REMOUNT)
 670                 sflags |= MS_REMOUNT;
 671 
 672         /* Convert some Linux flags to Solaris option strings. */
 673         if ((flags & LX_MS_NODEV) &&
 674             ((rv = i_add_option("nodev", options, sizeof (options))) != 0))
 675                 return (rv);
 676         if ((flags & LX_MS_NOEXEC) &&
 677             ((rv = i_add_option("noexec", options, sizeof (options))) != 0))
 678                 return (rv);
 679         if ((flags & LX_MS_NOATIME) &&
 680             ((rv = i_add_option("noatime", options, sizeof (options))) != 0))
 681                 return (rv);
 682 
 683         lx_debug("\tsolaris mount fstype: %s", fstype);
 684         lx_debug("\tsolaris mount options: \"%s\"", options);
 685 
 686         return (mount(source, target, sflags, fstype, sdataptr, sdatalen,
 687             options, sizeof (options)) ? -errno : 0);
 688 }
 689 
 690 /*
 691  * umount() is identical, though it is implemented on top of umount2() in
 692  * Solaris so it cannot be a pass-thru system call.
 693  */
 694 int
 695 lx_umount(uintptr_t p1)
 696 {
 697         return (umount((char *)p1) ? -errno : 0);
 698 }
 699 
 700 /*
 701  * The Linux umount2() system call is identical but has a different value for
 702  * MNT_FORCE (the logical equivalent to MS_FORCE).
 703  */
 704 #define LX_MNT_FORCE    0x1
 705 
 706 int
 707 lx_umount2(uintptr_t p1, uintptr_t p2)
 708 {
 709         char *path = (char *)p1;
 710         int flags = 0;
 711 
 712         if (p2 & ~LX_MNT_FORCE)
 713                 return (-EINVAL);
 714 
 715         if (p2 & LX_MNT_FORCE)
 716                 flags |= MS_FORCE;
 717 
 718         return (umount2(path, flags) ? -errno : 0);
 719 }