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 * autod_mount.c
23 *
24 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 #pragma ident "%Z%%M% %I% %E% SMI"
29
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/param.h>
37 #include <errno.h>
38 #include <pwd.h>
39 #include <netinet/in.h>
40 #include <netdb.h>
41 #include <sys/tiuser.h>
42 #include <locale.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <sys/mntent.h>
46 #include <sys/mnttab.h>
47 #include <sys/wait.h>
48 #include <sys/mount.h>
49 #include <sys/fs/autofs.h>
50 #include <nfs/nfs.h>
51 #include <thread.h>
52 #include <limits.h>
53 #include <assert.h>
54 #include <fcntl.h>
55 #include <strings.h>
56
57 #include "automount.h"
58 #include "replica.h"
59
60 static int unmount_mntpnt(struct mnttab *);
61 static int call_fork_exec(char *, char *, char **, int);
62 static void remove_browse_options(char *);
63 static int inherit_options(char *, char **);
64
65 int
do_mount1(char * mapname,char * key,char * subdir,char * mapopts,char * path,uint_t isdirect,uid_t uid,action_list ** alpp,int flags)66 do_mount1(
67 char *mapname,
68 char *key,
69 char *subdir,
70 char *mapopts,
71 char *path,
72 uint_t isdirect,
73 uid_t uid,
74 action_list **alpp,
75 int flags)
76 {
77 struct mapline ml;
78 struct mapent *me, *mapents = NULL;
79 char mntpnt[MAXPATHLEN];
80 char spec_mntpnt[MAXPATHLEN];
81 int err = 0;
82 char *private; /* fs specific data. eg prevhost in case of nfs */
83 int mount_ok = 0;
84 ssize_t len;
85 action_list *alp, *prev, *tmp;
86 char root[MAXPATHLEN];
87 int overlay = 1;
88 char next_subdir[MAXPATHLEN];
89 bool_t mount_access = TRUE;
90 bool_t iswildcard;
91 bool_t isrestricted = hasrestrictopt(mapopts);
92 char *stack[STACKSIZ];
93 char **stkptr = stack;
94
95 retry:
96 iswildcard = FALSE;
97
98 /* initialize the stack of open files for this thread */
99 stack_op(INIT, NULL, stack, &stkptr);
100
101 err = getmapent(key, mapname, &ml, stack, &stkptr, &iswildcard,
102 isrestricted);
103 if (err == 0) {
104 mapents = parse_entry(key, mapname, mapopts, &ml,
105 subdir, isdirect, mount_access);
106 }
107
108 if (trace) {
109 struct mapfs *mfs;
110 trace_prt(1, " do_mount1:\n");
111 for (me = mapents; me; me = me->map_next) {
112 trace_prt(1, " (%s,%s)\t%s%s%s\n",
113 me->map_fstype ? me->map_fstype : "",
114 me->map_mounter ? me->map_mounter : "",
115 path ? path : "",
116 me->map_root ? me->map_root : "",
117 me->map_mntpnt ? me->map_mntpnt : "");
118 trace_prt(0, "\t\t-%s\n",
119 me->map_mntopts ? me->map_mntopts : "");
120
121 for (mfs = me->map_fs; mfs; mfs = mfs->mfs_next)
122 trace_prt(0, "\t\t%s:%s\tpenalty=%d\n",
123 mfs->mfs_host ? mfs->mfs_host: "",
124 mfs->mfs_dir ? mfs->mfs_dir : "",
125 mfs->mfs_penalty);
126 }
127 }
128
129 *alpp = NULL;
130
131 /*
132 * Each mapent in the list describes a mount to be done.
133 * Normally there's just a single entry, though in the
134 * case of /net mounts there may be many entries, that
135 * must be mounted as a hierarchy. For each mount the
136 * automountd must make sure the required mountpoint
137 * exists and invoke the appropriate mount command for
138 * the fstype.
139 */
140 private = "";
141 for (me = mapents; me && !err; me = me->map_next) {
142 len = snprintf(mntpnt, sizeof (mntpnt), "%s%s%s", path,
143 mapents->map_root, me->map_mntpnt);
144
145 if (len >= sizeof (mntpnt)) {
146 free_mapent(mapents);
147 return (ENAMETOOLONG);
148 }
149 /*
150 * remove trailing /'s from mountpoint to avoid problems
151 * stating a directory with two or more trailing slashes.
152 * This will let us mount directories from machines
153 * which export with two or more slashes (apollo for instance).
154 */
155 len -= 1;
156 while (mntpnt[len] == '/')
157 mntpnt[len--] = '\0';
158
159 (void) strcpy(spec_mntpnt, mntpnt);
160
161 if (isrestricted &&
162 inherit_options(mapopts, &me->map_mntopts) != 0) {
163 syslog(LOG_ERR, "malloc of options failed");
164 free_mapent(mapents);
165 return (EAGAIN);
166 }
167
168 if (strcmp(me->map_fstype, MNTTYPE_NFS) == 0) {
169 remove_browse_options(me->map_mntopts);
170 if (flags == DOMOUNT_KERNEL) {
171 alp = (action_list *)malloc(
172 sizeof (action_list));
173 if (alp == NULL) {
174 syslog(LOG_ERR,
175 "malloc of alp failed");
176 continue;
177 }
178 memset(alp, 0, sizeof (action_list));
179 } else
180 alp = NULL;
181 err =
182 mount_nfs(me, spec_mntpnt, private, overlay, uid,
183 &alp);
184 /*
185 * We must retry if we don't have access to the
186 * root file system and there are other
187 * following mapents. The reason we can't
188 * continue because the rest of the mapent list
189 * depends on whether mount_access is TRUE or FALSE.
190 */
191 if (err == NFSERR_ACCES && me->map_next != NULL) {
192 /*
193 * don't expect mount_access to be
194 * FALSE here, but we do a check
195 * anyway.
196 */
197 if (mount_access == TRUE) {
198 mount_access = FALSE;
199 err = 0;
200 free_mapent(mapents);
201 if (alp) {
202 free(alp);
203 alp = NULL;
204 }
205 goto retry;
206 }
207 }
208 if (alp) {
209 if (*alpp == NULL)
210 *alpp = alp;
211 else {
212 for (tmp = *alpp; tmp != NULL;
213 tmp = tmp->next)
214 prev = tmp;
215 prev->next = alp;
216 }
217 }
218 mount_ok = !err;
219 } else if (strcmp(me->map_fstype, MNTTYPE_AUTOFS) == 0) {
220 if (isdirect) {
221 len = strlcpy(root, path, sizeof (root));
222 } else {
223 len = snprintf(root, sizeof (root), "%s/%s",
224 path, key);
225 }
226 if (len >= sizeof (root)) {
227 free_mapent(mapents);
228 return (ENAMETOOLONG);
229 }
230
231 alp = (action_list *)malloc(sizeof (action_list));
232 if (alp == NULL) {
233 syslog(LOG_ERR, "malloc of alp failed");
234 continue;
235 }
236 memset(alp, 0, sizeof (action_list));
237
238 /*
239 * get the next subidr, but only if its a modified
240 * or faked autofs mount
241 */
242 if (me->map_modified || me->map_faked) {
243 len = snprintf(next_subdir,
244 sizeof (next_subdir), "%s%s", subdir,
245 me->map_mntpnt);
246 } else {
247 next_subdir[0] = '\0';
248 len = 0;
249 }
250
251 if (trace > 2)
252 trace_prt(1, " root=%s\t next_subdir=%s\n",
253 root, next_subdir);
254 if (len < sizeof (next_subdir)) {
255 err = mount_autofs(me, spec_mntpnt, alp,
256 root, next_subdir, key);
257 } else {
258 err = ENAMETOOLONG;
259 }
260 if (err == 0) {
261 /*
262 * append to action list
263 */
264 mount_ok++;
265 if (*alpp == NULL)
266 *alpp = alp;
267 else {
268 for (tmp = *alpp; tmp != NULL;
269 tmp = tmp->next)
270 prev = tmp;
271 prev->next = alp;
272 }
273 } else {
274 free(alp);
275 mount_ok = 0;
276 }
277 } else if (strcmp(me->map_fstype, MNTTYPE_LOFS) == 0) {
278 remove_browse_options(me->map_mntopts);
279 err = loopbackmount(me->map_fs->mfs_dir, spec_mntpnt,
280 me->map_mntopts, overlay);
281 mount_ok = !err;
282 } else {
283 remove_browse_options(me->map_mntopts);
284 err = mount_generic(me->map_fs->mfs_dir,
285 me->map_fstype, me->map_mntopts,
286 spec_mntpnt, overlay);
287 mount_ok = !err;
288 }
289 }
290 if (mapents)
291 free_mapent(mapents);
292
293 /*
294 * If an error occurred,
295 * the filesystem doesn't exist, or could not be
296 * mounted. Return EACCES to autofs indicating that
297 * the mountpoint can not be accessed if this is not
298 * a wildcard access. If it is a wildcard access we
299 * return ENOENT since the lookup that triggered
300 * this mount request will fail and the entry will not
301 * be available.
302 */
303 if (mount_ok) {
304 /*
305 * No error occurred, return 0 to indicate success.
306 */
307 err = 0;
308 } else {
309 /*
310 * The filesystem does not exist or could not be mounted.
311 * Return ENOENT if the lookup was triggered by a wildcard
312 * access. Wildcard entries only exist if they can be
313 * mounted. They can not be listed otherwise (through
314 * a readdir(2)).
315 * Return EACCES if the lookup was not triggered by a
316 * wildcard access. Map entries that are explicitly defined
317 * in maps are visible via readdir(2), therefore we return
318 * EACCES to indicate that the entry exists, but the directory
319 * can not be opened. This is the same behavior of a Unix
320 * directory that exists, but has its execute bit turned off.
321 * The directory is there, but the user does not have access
322 * to it.
323 */
324 if (iswildcard)
325 err = ENOENT;
326 else
327 err = EACCES;
328 }
329 return (err);
330 }
331
332 #define ARGV_MAX 16
333 #define VFS_PATH "/usr/lib/fs"
334
335 int
mount_generic(special,fstype,opts,mntpnt,overlay)336 mount_generic(special, fstype, opts, mntpnt, overlay)
337 char *special, *fstype, *opts, *mntpnt;
338 int overlay;
339 {
340 struct mnttab m;
341 struct stat stbuf;
342 int i, res;
343 char *newargv[ARGV_MAX];
344
345 if (trace > 1) {
346 trace_prt(1, " mount: %s %s %s %s\n",
347 special, mntpnt, fstype, opts);
348 }
349
350 if (stat(mntpnt, &stbuf) < 0) {
351 syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt);
352 return (ENOENT);
353 }
354
355 i = 2;
356
357 if (overlay)
358 newargv[i++] = "-O";
359
360 /*
361 * Use "quiet" option to suppress warnings about unsupported
362 * mount options.
363 */
364 newargv[i++] = "-q";
365
366 if (opts && *opts) {
367 m.mnt_mntopts = opts;
368 if (hasmntopt(&m, MNTOPT_RO) != NULL)
369 newargv[i++] = "-r";
370 newargv[i++] = "-o";
371 newargv[i++] = opts;
372 }
373 newargv[i++] = "--";
374 newargv[i++] = special;
375 newargv[i++] = mntpnt;
376 newargv[i] = NULL;
377 res = call_fork_exec(fstype, "mount", newargv, verbose);
378 if (res == 0 && trace > 1) {
379 if (stat(mntpnt, &stbuf) == 0) {
380 trace_prt(1, " mount of %s dev=%x rdev=%x OK\n",
381 mntpnt, stbuf.st_dev, stbuf.st_rdev);
382 } else {
383 trace_prt(1, " failed to stat %s\n", mntpnt);
384 }
385 }
386 return (res);
387 }
388
389 void
automountd_do_fork_exec(void * cookie,char * argp,size_t arg_size,door_desc_t * dfd,uint_t n_desc)390 automountd_do_fork_exec(void *cookie, char *argp, size_t arg_size,
391 door_desc_t *dfd, uint_t n_desc)
392 {
393 int stat_loc;
394 int fd = 0;
395 struct stat stbuf;
396 int res;
397 int child_pid;
398 command_t *command;
399 char *newargv[ARGV_MAX];
400 int i;
401
402
403 command = (command_t *)argp;
404 if (sizeof (*command) != arg_size) {
405 res = EINVAL;
406 door_return((char *)&res, sizeof (res), NULL, 0);
407 }
408
409 switch ((child_pid = fork1())) {
410 case -1:
411 syslog(LOG_ERR, "Cannot fork: %m");
412 res = errno;
413 break;
414 case 0:
415 /*
416 * Child
417 */
418 (void) setsid();
419 fd = open(command->console ? "/dev/console" : "/dev/null",
420 O_WRONLY);
421 if (fd != -1) {
422 (void) dup2(fd, 1);
423 (void) dup2(fd, 2);
424 (void) close(fd);
425 }
426
427 for (i = 0; *command->argv[i]; i++) {
428 newargv[i] = strdup(command->argv[i]);
429 if (newargv[i] == (char *)NULL) {
430 syslog(LOG_ERR, "failed to copy argument '%s'"
431 " of %s: %m", command->argv[i],
432 command->file);
433 _exit(errno);
434 }
435 }
436 newargv[i] = NULL;
437
438 (void) execv(command->file, newargv);
439 if (errno == EACCES)
440 syslog(LOG_ERR, "exec %s: %m", command->file);
441
442 _exit(errno);
443 default:
444 /*
445 * Parent
446 */
447 (void) waitpid(child_pid, &stat_loc, WUNTRACED);
448
449 if (WIFEXITED(stat_loc)) {
450 if (trace > 1) {
451 trace_prt(1,
452 " fork_exec: returns exit status %d\n",
453 WEXITSTATUS(stat_loc));
454 }
455
456 res = WEXITSTATUS(stat_loc);
457 } else if (WIFSIGNALED(stat_loc)) {
458 if (trace > 1)
459 trace_prt(1,
460 " fork_exec: returns signal status %d\n",
461 WTERMSIG(stat_loc));
462 res = 1;
463 } else {
464 if (trace > 1)
465 trace_prt(1,
466 " fork_exec: returns unknown status\n");
467 res = 1;
468 }
469
470 }
471 door_return((char *)&res, sizeof (res), NULL, 0);
472 trace_prt(1, "automountd_do_fork_exec, door return failed %s, %s\n",
473 command->file, strerror(errno));
474 door_return(NULL, 0, NULL, 0);
475 }
476
477 int
do_unmount1(ur)478 do_unmount1(ur)
479 umntrequest *ur;
480 {
481
482 struct mnttab m;
483 int res = 0;
484
485 m.mnt_special = ur->mntresource;
486 m.mnt_mountp = ur->mntpnt;
487 m.mnt_fstype = ur->fstype;
488 m.mnt_mntopts = ur->mntopts;
489 /*
490 * Special case for NFS mounts.
491 * Don't want to attempt unmounts from
492 * a dead server. If any member of a
493 * hierarchy belongs to a dead server
494 * give up (try later).
495 */
496 if (strcmp(ur->fstype, MNTTYPE_NFS) == 0) {
497 struct replica *list;
498 int i, n;
499 bool_t pubopt = FALSE;
500 int nfs_port;
501 int got_port;
502
503 /*
504 * See if a port number was specified. If one was
505 * specified that is too large to fit in 16 bits, truncate
506 * the high-order bits (for historical compatibility). Use
507 * zero to indicate "no port specified".
508 */
509 got_port = nopt(&m, MNTOPT_PORT, &nfs_port);
510 if (!got_port)
511 nfs_port = 0;
512 nfs_port &= USHRT_MAX;
513
514 if (hasmntopt(&m, MNTOPT_PUBLIC))
515 pubopt = TRUE;
516
517 list = parse_replica(ur->mntresource, &n);
518 if (list == NULL) {
519 if (n >= 0)
520 syslog(LOG_ERR, "Memory allocation failed: %m");
521 res = 1;
522 goto done;
523 }
524
525 for (i = 0; i < n; i++) {
526 if (pingnfs(list[i].host, 1, NULL, 0, nfs_port,
527 pubopt, list[i].path, NULL) != RPC_SUCCESS) {
528 res = 1;
529 free_replica(list, n);
530 goto done;
531 }
532 }
533 free_replica(list, n);
534 }
535
536 res = unmount_mntpnt(&m);
537
538 done: return (res);
539 }
540
541 static int
unmount_mntpnt(mnt)542 unmount_mntpnt(mnt)
543 struct mnttab *mnt;
544 {
545 char *fstype = mnt->mnt_fstype;
546 char *mountp = mnt->mnt_mountp;
547 char *newargv[ARGV_MAX];
548 int res;
549
550 if (strcmp(fstype, MNTTYPE_NFS) == 0) {
551 res = nfsunmount(mnt);
552 } else if (strcmp(fstype, MNTTYPE_LOFS) == 0) {
553 if ((res = umount(mountp)) < 0)
554 res = errno;
555 } else {
556 newargv[2] = mountp;
557 newargv[3] = NULL;
558
559 res = call_fork_exec(fstype, "umount", newargv, verbose);
560 if (res == ENOENT) {
561 /*
562 * filesystem specific unmount command not found
563 */
564 if ((res = umount(mountp)) < 0)
565 res = errno;
566 }
567 }
568
569 if (trace > 1)
570 trace_prt(1, " unmount %s %s\n",
571 mountp, res ? "failed" : "OK");
572 return (res);
573 }
574
575 /*
576 * Remove the autofs specific options 'browse', 'nobrowse' and
577 * 'restrict' from 'opts'.
578 */
579 static void
remove_browse_options(char * opts)580 remove_browse_options(char *opts)
581 {
582 char *p, *pb;
583 char buf[MAXOPTSLEN], new[MAXOPTSLEN];
584 char *placeholder;
585
586 new[0] = '\0';
587 (void) strcpy(buf, opts);
588 pb = buf;
589 while (p = (char *)strtok_r(pb, ",", &placeholder)) {
590 pb = NULL;
591 if (strcmp(p, MNTOPT_NOBROWSE) != 0 &&
592 strcmp(p, MNTOPT_BROWSE) != 0 &&
593 strcmp(p, MNTOPT_RESTRICT) != 0) {
594 if (new[0] != '\0')
595 (void) strcat(new, ",");
596 (void) strcat(new, p);
597 }
598 }
599 (void) strcpy(opts, new);
600 }
601
602 static const char *restropts[] = {
603 RESTRICTED_MNTOPTS
604 };
605 #define NROPTS (sizeof (restropts)/sizeof (restropts[0]))
606
607 static int
inherit_options(char * opts,char ** mapentopts)608 inherit_options(char *opts, char **mapentopts)
609 {
610 int i;
611 char *new;
612 struct mnttab mtmap;
613 struct mnttab mtopt;
614
615 size_t len = strlen(*mapentopts);
616
617 for (i = 0; i < NROPTS; i++)
618 len += strlen(restropts[i]);
619
620 /* "," for each new option plus the trailing NUL */
621 len += NROPTS + 1;
622
623 new = malloc(len);
624 if (new == 0)
625 return (-1);
626
627 (void) strcpy(new, *mapentopts);
628
629 mtmap.mnt_mntopts = *mapentopts;
630 mtopt.mnt_mntopts = opts;
631
632 for (i = 0; i < NROPTS; i++) {
633 if (hasmntopt(&mtopt, (char *)restropts[i]) != NULL &&
634 hasmntopt(&mtmap, (char *)restropts[i]) == NULL) {
635 if (*new != '\0')
636 (void) strcat(new, ",");
637 (void) strcat(new, restropts[i]);
638 }
639 }
640 free(*mapentopts);
641 *mapentopts = new;
642 return (0);
643 }
644
645 bool_t
hasrestrictopt(char * opts)646 hasrestrictopt(char *opts)
647 {
648 struct mnttab mt;
649
650 mt.mnt_mntopts = opts;
651
652 return (hasmntopt(&mt, MNTOPT_RESTRICT) != NULL);
653 }
654
655 static int
call_fork_exec(fstype,cmd,newargv,console)656 call_fork_exec(fstype, cmd, newargv, console)
657 char *fstype;
658 char *cmd;
659 char **newargv;
660 int console;
661 {
662 command_t command;
663 door_arg_t darg;
664 char path[MAXPATHLEN];
665 struct stat stbuf;
666 int ret;
667 int sz;
668 int status;
669 int i;
670
671 bzero(&command, sizeof (command));
672 /* build the full path name of the fstype dependent command */
673 (void) snprintf(path, MAXPATHLEN, "%s/%s/%s", VFS_PATH, fstype, cmd);
674
675 if (stat(path, &stbuf) != 0) {
676 ret = errno;
677 return (ret);
678 }
679
680 strlcpy(command.file, path, MAXPATHLEN);
681 strlcpy(command.argv[0], path, MAXOPTSLEN);
682 for (i = 2; newargv[i]; i++) {
683 strlcpy(command.argv[i-1], newargv[i], MAXOPTSLEN);
684 }
685 if (trace > 1) {
686 trace_prt(1, " call_fork_exec: %s ", command.file);
687 for (i = 0; *command.argv[i]; i++)
688 trace_prt(0, "%s ", command.argv[i]);
689 trace_prt(0, "\n");
690 }
691
692 command.console = console;
693
694 darg.data_ptr = (char *)&command;
695 darg.data_size = sizeof (command);
696 darg.desc_ptr = NULL;
697 darg.desc_num = 0;
698 darg.rbuf = (char *)&status;
699 darg.rsize = sizeof (status);
700
701 ret = door_call(did_fork_exec, &darg);
702 if (trace > 1) {
703 trace_prt(1, " call_fork_exec: door_call failed %d\n", ret);
704 }
705
706 return (status);
707 }
708