xref: /titanic_52/usr/src/cmd/fs.d/autofs/autod_mount.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  *	autod_mount.c
24  *
25  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <string.h>
34 #include <syslog.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/param.h>
38 #include <errno.h>
39 #include <pwd.h>
40 #include <netinet/in.h>
41 #include <netdb.h>
42 #include <sys/tiuser.h>
43 #include <locale.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <sys/mntent.h>
47 #include <sys/mnttab.h>
48 #include <sys/wait.h>
49 #include <sys/mount.h>
50 #include <sys/fs/autofs.h>
51 #include <nfs/nfs.h>
52 #include <thread.h>
53 #include <limits.h>
54 #include <assert.h>
55 #include <fcntl.h>
56 
57 #include "automount.h"
58 #include "replica.h"
59 
60 static int unmount_mntpnt(struct mnttab *);
61 static int 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(mapname, key, subdir, mapopts, path, isdirect, alpp, cred)
67 	char *mapname;
68 	char *key;
69 	char *subdir;
70 	char *mapopts;
71 	char *path;
72 	uint_t isdirect;
73 	struct action_list **alpp;
74 	struct authunix_parms *cred;
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 > 1) {
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 			err =
170 			    mount_nfs(me, spec_mntpnt, private, overlay, cred);
171 			/*
172 			 * We must retry if we don't have access to the
173 			 * root file system and there are other
174 			 * following mapents. The reason we can't
175 			 * continue because the rest of the mapent list
176 			 * depends on whether mount_access is TRUE or FALSE.
177 			 */
178 			if (err == NFSERR_ACCES && me->map_next != NULL) {
179 				/*
180 				 * don't expect mount_access to be
181 				 * FALSE here, but we do a check
182 				 * anyway.
183 				 */
184 				if (mount_access == TRUE) {
185 					mount_access = FALSE;
186 					err = 0;
187 					free_mapent(mapents);
188 					goto retry;
189 				}
190 			}
191 			mount_ok = !err;
192 		} else if (strcmp(me->map_fstype, MNTTYPE_AUTOFS) == 0) {
193 			if (isdirect) {
194 				len = strlcpy(root, path, sizeof (root));
195 			} else {
196 				len = snprintf(root, sizeof (root), "%s/%s",
197 				    path, key);
198 			}
199 			if (len >= sizeof (root)) {
200 				free_mapent(mapents);
201 				return (ENAMETOOLONG);
202 			}
203 
204 			alp = (action_list *)malloc(sizeof (action_list));
205 			if (alp == NULL) {
206 				syslog(LOG_ERR, "malloc of alp failed");
207 				continue;
208 			}
209 			memset(alp, 0, sizeof (action_list));
210 
211 			/*
212 			 * get the next subidr, but only if its a modified
213 			 * or faked autofs mount
214 			 */
215 			if (me->map_modified || me->map_faked) {
216 				len = snprintf(next_subdir,
217 					sizeof (next_subdir), "%s%s", subdir,
218 					me->map_mntpnt);
219 			} else {
220 				next_subdir[0] = '\0';
221 				len = 0;
222 			}
223 
224 			if (trace > 2)
225 				trace_prt(1, "  root=%s\t next_subdir=%s\n",
226 						root, next_subdir);
227 			if (len < sizeof (next_subdir)) {
228 				err = mount_autofs(me, spec_mntpnt, alp,
229 					root, next_subdir, key);
230 			} else {
231 				err = ENAMETOOLONG;
232 			}
233 			if (err == 0) {
234 				/*
235 				 * append to action list
236 				 */
237 				mount_ok++;
238 				if (*alpp == NULL)
239 					*alpp = alp;
240 				else {
241 					for (tmp = *alpp; tmp != NULL;
242 					    tmp = tmp->next)
243 						prev = tmp;
244 					prev->next = alp;
245 				}
246 			} else {
247 				free(alp);
248 				mount_ok = 0;
249 			}
250 		} else if (strcmp(me->map_fstype, MNTTYPE_LOFS) == 0) {
251 			remove_browse_options(me->map_mntopts);
252 			err = loopbackmount(me->map_fs->mfs_dir, spec_mntpnt,
253 					    me->map_mntopts, overlay);
254 			mount_ok = !err;
255 		} else {
256 			remove_browse_options(me->map_mntopts);
257 			err = mount_generic(me->map_fs->mfs_dir,
258 					    me->map_fstype, me->map_mntopts,
259 					    spec_mntpnt, overlay);
260 			mount_ok = !err;
261 		}
262 	}
263 	if (mapents)
264 		free_mapent(mapents);
265 
266 	/*
267 	 * If an error occurred,
268 	 * the filesystem doesn't exist, or could not be
269 	 * mounted.  Return EACCES to autofs indicating that
270 	 * the mountpoint can not be accessed if this is not
271 	 * a wildcard access.  If it is a wildcard access we
272 	 * return ENOENT since the lookup that triggered
273 	 * this mount request will fail and the entry will not
274 	 * be available.
275 	 */
276 	if (mount_ok) {
277 		/*
278 		 * No error occurred, return 0 to indicate success.
279 		 */
280 		err = 0;
281 	} else {
282 		/*
283 		 * The filesystem does not exist or could not be mounted.
284 		 * Return ENOENT if the lookup was triggered by a wildcard
285 		 * access.  Wildcard entries only exist if they can be
286 		 * mounted.  They can not be listed otherwise (through
287 		 * a readdir(2)).
288 		 * Return EACCES if the lookup was not triggered by a
289 		 * wildcard access.  Map entries that are explicitly defined
290 		 * in maps are visible via readdir(2), therefore we return
291 		 * EACCES to indicate that the entry exists, but the directory
292 		 * can not be opened.  This is the same behavior of a Unix
293 		 * directory that exists, but has its execute bit turned off.
294 		 * The directory is there, but the user does not have access
295 		 * to it.
296 		 */
297 		if (iswildcard)
298 			err = ENOENT;
299 		else
300 			err = EACCES;
301 	}
302 	return (err);
303 }
304 
305 #define	ARGV_MAX	16
306 #define	VFS_PATH	"/usr/lib/fs"
307 
308 int
309 mount_generic(special, fstype, opts, mntpnt, overlay)
310 	char *special, *fstype, *opts, *mntpnt;
311 	int overlay;
312 {
313 	struct mnttab m;
314 	struct stat stbuf;
315 	int i, res;
316 	char *newargv[ARGV_MAX];
317 
318 	if (trace > 1) {
319 		trace_prt(1, "  mount: %s %s %s %s\n",
320 			special, mntpnt, fstype, opts);
321 	}
322 
323 	if (stat(mntpnt, &stbuf) < 0) {
324 		syslog(LOG_ERR, "Couldn't stat %s: %m", mntpnt);
325 		return (ENOENT);
326 	}
327 
328 	i = 2;
329 
330 	if (overlay)
331 		newargv[i++] = "-O";
332 
333 	/*
334 	 *  Use "quiet" option to suppress warnings about unsupported
335 	 *  mount options.
336 	 */
337 	newargv[i++] = "-q";
338 
339 	if (opts && *opts) {
340 		m.mnt_mntopts = opts;
341 		if (hasmntopt(&m, MNTOPT_RO) != NULL)
342 			newargv[i++] = "-r";
343 		newargv[i++] = "-o";
344 		newargv[i++] = opts;
345 	}
346 	newargv[i++] = "--";
347 	newargv[i++] = special;
348 	newargv[i++] = mntpnt;
349 	newargv[i] = NULL;
350 	res = fork_exec(fstype, "mount", newargv, verbose);
351 	if (res == 0 && trace > 1) {
352 		if (stat(mntpnt, &stbuf) == 0) {
353 			trace_prt(1, "  mount of %s dev=%x rdev=%x OK\n",
354 				mntpnt, stbuf.st_dev, stbuf.st_rdev);
355 		} else {
356 			trace_prt(1, "  failed to stat %s\n", mntpnt);
357 		}
358 	}
359 	return (res);
360 }
361 
362 static int
363 fork_exec(fstype, cmd, newargv, console)
364 	char *fstype;
365 	char *cmd;
366 	char **newargv;
367 	int console;
368 {
369 	char path[MAXPATHLEN];
370 	int i;
371 	int stat_loc;
372 	int fd = 0;
373 	struct stat stbuf;
374 	int res;
375 	int child_pid;
376 
377 	/* build the full path name of the fstype dependent command */
378 	(void) sprintf(path, "%s/%s/%s", VFS_PATH, fstype, cmd);
379 
380 	if (stat(path, &stbuf) != 0) {
381 		res = errno;
382 		return (res);
383 	}
384 
385 	if (trace > 1) {
386 		trace_prt(1, "  fork_exec: %s ", path);
387 		for (i = 2; newargv[i]; i++)
388 			trace_prt(0, "%s ", newargv[i]);
389 		trace_prt(0, "\n");
390 	}
391 
392 
393 	newargv[1] = cmd;
394 	switch ((child_pid = fork1())) {
395 	case -1:
396 		syslog(LOG_ERR, "Cannot fork: %m");
397 		return (errno);
398 	case 0:
399 		/*
400 		 * Child
401 		 */
402 		(void) setsid();
403 		fd = open(console ? "/dev/console" : "/dev/null", O_WRONLY);
404 		if (fd != -1) {
405 			(void) dup2(fd, 1);
406 			(void) dup2(fd, 2);
407 			(void) close(fd);
408 		}
409 
410 		(void) execv(path, &newargv[1]);
411 		if (errno == EACCES)
412 			syslog(LOG_ERR, "exec %s: %m", path);
413 
414 		_exit(errno);
415 	default:
416 		/*
417 		 * Parent
418 		 */
419 		(void) waitpid(child_pid, &stat_loc, WUNTRACED);
420 
421 		if (WIFEXITED(stat_loc)) {
422 			if (trace > 1) {
423 				trace_prt(1,
424 				    "  fork_exec: returns exit status %d\n",
425 				    WEXITSTATUS(stat_loc));
426 			}
427 
428 			return (WEXITSTATUS(stat_loc));
429 		} else
430 		if (WIFSIGNALED(stat_loc)) {
431 			if (trace > 1) {
432 				trace_prt(1,
433 				    "  fork_exec: returns signal status %d\n",
434 				    WTERMSIG(stat_loc));
435 			}
436 		} else {
437 			if (trace > 1)
438 				trace_prt(1,
439 				    "  fork_exec: returns unknown status\n");
440 		}
441 
442 		return (1);
443 	}
444 }
445 
446 int
447 do_unmount1(ur)
448 	umntrequest *ur;
449 {
450 
451 	struct mnttab m;
452 	int res = 0;
453 
454 	m.mnt_special = ur->mntresource;
455 	m.mnt_mountp = ur->mntpnt;
456 	m.mnt_fstype = ur->fstype;
457 	m.mnt_mntopts = ur->mntopts;
458 	/*
459 	 * Special case for NFS mounts.
460 	 * Don't want to attempt unmounts from
461 	 * a dead server.  If any member of a
462 	 * hierarchy belongs to a dead server
463 	 * give up (try later).
464 	 */
465 	if (strcmp(ur->fstype, MNTTYPE_NFS) == 0) {
466 		struct replica *list;
467 		int i, n;
468 		bool_t pubopt = FALSE;
469 		int nfs_port;
470 		int got_port;
471 
472 		/*
473 		 * See if a port number was specified.  If one was
474 		 * specified that is too large to fit in 16 bits, truncate
475 		 * the high-order bits (for historical compatibility).  Use
476 		 * zero to indicate "no port specified".
477 		 */
478 		got_port = nopt(&m, MNTOPT_PORT, &nfs_port);
479 		if (!got_port)
480 			nfs_port = 0;
481 		nfs_port &= USHRT_MAX;
482 
483 		if (hasmntopt(&m, MNTOPT_PUBLIC))
484 			pubopt = TRUE;
485 
486 		list = parse_replica(ur->mntresource, &n);
487 		if (list == NULL) {
488 			if (n >= 0)
489 				syslog(LOG_ERR, "Memory allocation failed: %m");
490 			res = 1;
491 			goto done;
492 		}
493 
494 		for (i = 0; i < n; i++) {
495 			if (pingnfs(list[i].host, 1, NULL, 0, nfs_port,
496 			    pubopt, list[i].path, NULL) != RPC_SUCCESS) {
497 				res = 1;
498 				free_replica(list, n);
499 				goto done;
500 			}
501 		}
502 		free_replica(list, n);
503 	}
504 
505 	res = unmount_mntpnt(&m);
506 
507 done:	return (res);
508 }
509 
510 static int
511 unmount_mntpnt(mnt)
512 	struct mnttab *mnt;
513 {
514 	char *fstype = mnt->mnt_fstype;
515 	char *mountp = mnt->mnt_mountp;
516 	char *newargv[ARGV_MAX];
517 	int res;
518 
519 	if (strcmp(fstype, MNTTYPE_NFS) == 0) {
520 		res = nfsunmount(mnt);
521 	} else if (strcmp(fstype, MNTTYPE_LOFS) == 0) {
522 		if ((res = umount(mountp)) < 0)
523 			res = errno;
524 	} else {
525 		newargv[2] = mountp;
526 		newargv[3] = NULL;
527 
528 		res = fork_exec(fstype, "umount", newargv, verbose);
529 		if (res == ENOENT) {
530 			/*
531 			 * filesystem specific unmount command not found
532 			 */
533 			if ((res = umount(mountp)) < 0)
534 				res = errno;
535 		}
536 	}
537 
538 	if (trace > 1)
539 		trace_prt(1, "  unmount %s %s\n",
540 			mountp, res ? "failed" : "OK");
541 	return (res);
542 }
543 
544 /*
545  * Remove the autofs specific options 'browse', 'nobrowse' and
546  * 'restrict' from 'opts'.
547  */
548 static void
549 remove_browse_options(char *opts)
550 {
551 	char *p, *pb;
552 	char buf[MAXOPTSLEN], new[MAXOPTSLEN];
553 	char *placeholder;
554 
555 	new[0] = '\0';
556 	(void) strcpy(buf, opts);
557 	pb = buf;
558 	while (p = (char *)strtok_r(pb, ",", &placeholder)) {
559 		pb = NULL;
560 		if (strcmp(p, MNTOPT_NOBROWSE) != 0 &&
561 		    strcmp(p, MNTOPT_BROWSE) != 0 &&
562 		    strcmp(p, MNTOPT_RESTRICT) != 0) {
563 			if (new[0] != '\0')
564 				(void) strcat(new, ",");
565 			(void) strcat(new, p);
566 		}
567 	}
568 	(void) strcpy(opts, new);
569 }
570 
571 static const char *restropts[] = {
572 	RESTRICTED_MNTOPTS
573 };
574 #define	NROPTS	(sizeof (restropts)/sizeof (restropts[0]))
575 
576 static int
577 inherit_options(char *opts, char **mapentopts)
578 {
579 	int i;
580 	char *new;
581 	struct mnttab mtmap;
582 	struct mnttab mtopt;
583 
584 	size_t len = strlen(*mapentopts);
585 
586 	for (i = 0; i < NROPTS; i++)
587 		len += strlen(restropts[i]);
588 
589 	/* "," for each new option plus the trailing NUL */
590 	len += NROPTS + 1;
591 
592 	new = malloc(len);
593 	if (new == 0)
594 		return (-1);
595 
596 	(void) strcpy(new, *mapentopts);
597 
598 	mtmap.mnt_mntopts = *mapentopts;
599 	mtopt.mnt_mntopts = opts;
600 
601 	for (i = 0; i < NROPTS; i++) {
602 		if (hasmntopt(&mtopt, (char *)restropts[i]) != NULL &&
603 		    hasmntopt(&mtmap, (char *)restropts[i]) == NULL) {
604 			if (*new != '\0')
605 				(void) strcat(new, ",");
606 			(void) strcat(new, restropts[i]);
607 		}
608 	}
609 	free(*mapentopts);
610 	*mapentopts = new;
611 	return (0);
612 }
613 
614 bool_t
615 hasrestrictopt(char *opts)
616 {
617 	struct mnttab mt;
618 
619 	mt.mnt_mntopts = opts;
620 
621 	return (hasmntopt(&mt, MNTOPT_RESTRICT) != NULL);
622 }
623