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