xref: /illumos-gate/usr/src/cmd/fs.d/autofs/autod_mount.c (revision 9782b1c323d5daef57c9b4983fedfedd4b3be96f)
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
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
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
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
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
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
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
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
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
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