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