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 }