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