xref: /netbsd-src/usr.sbin/autofs/automount.c (revision 5628820aad1597d6c95795148f19b1e219c2967f)
1*5628820aStkusumi /*	$NetBSD: automount.c,v 1.4 2022/05/04 11:27:54 tkusumi Exp $	*/
2b985414bSchristos 
3b985414bSchristos /*-
4b985414bSchristos  * Copyright (c) 2017 The NetBSD Foundation, Inc.
5b985414bSchristos  * Copyright (c) 2016 The DragonFly Project
6b985414bSchristos  * Copyright (c) 2014 The FreeBSD Foundation
7b985414bSchristos  * All rights reserved.
8b985414bSchristos  *
9b985414bSchristos  * This code is derived from software contributed to The NetBSD Foundation
10b985414bSchristos  * by Tomohiro Kusumi <kusumi.tomohiro@gmail.com>.
11b985414bSchristos  *
12b985414bSchristos  * This software was developed by Edward Tomasz Napierala under sponsorship
13b985414bSchristos  * from the FreeBSD Foundation.
14b985414bSchristos  *
15b985414bSchristos  * Redistribution and use in source and binary forms, with or without
16b985414bSchristos  * modification, are permitted provided that the following conditions
17b985414bSchristos  * are met:
18b985414bSchristos  * 1. Redistributions of source code must retain the above copyright
19b985414bSchristos  *    notice, this list of conditions and the following disclaimer.
20b985414bSchristos  * 2. Redistributions in binary form must reproduce the above copyright
21b985414bSchristos  *    notice, this list of conditions and the following disclaimer in the
22b985414bSchristos  *    documentation and/or other materials provided with the distribution.
23b985414bSchristos  *
24b985414bSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25b985414bSchristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26b985414bSchristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27b985414bSchristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28b985414bSchristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29b985414bSchristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30b985414bSchristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31b985414bSchristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32b985414bSchristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33b985414bSchristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34b985414bSchristos  * SUCH DAMAGE.
35b985414bSchristos  */
36b985414bSchristos #include <sys/cdefs.h>
37*5628820aStkusumi __RCSID("$NetBSD: automount.c,v 1.4 2022/05/04 11:27:54 tkusumi Exp $");
38b985414bSchristos 
39b985414bSchristos #include <sys/types.h>
40b985414bSchristos #include <sys/mount.h>
41b985414bSchristos #include <sys/statvfs.h>
42b985414bSchristos #include <stdio.h>
43b985414bSchristos #include <stdlib.h>
44b985414bSchristos #include <string.h>
45b985414bSchristos #include <unistd.h>
46b985414bSchristos #include <fs/autofs/autofs_mount.h>
47b985414bSchristos 
48b985414bSchristos #include "common.h"
49b985414bSchristos 
50b985414bSchristos static int
unmount_by_statfs(const struct statvfs * sb,bool force)51b985414bSchristos unmount_by_statfs(const struct statvfs *sb, bool force)
52b985414bSchristos {
53b985414bSchristos 	int error, flags;
54b985414bSchristos 
55b985414bSchristos 	log_debugx("unmounting %s", sb->f_mntonname);
56b985414bSchristos 
57b985414bSchristos 	flags = 0;
58b985414bSchristos 	if (force)
59b985414bSchristos 		flags |= MNT_FORCE;
60b985414bSchristos 	error = unmount(sb->f_mntonname, flags);
61b985414bSchristos 	if (error != 0)
62b985414bSchristos 		log_warn("cannot unmount %s", sb->f_mntonname);
63b985414bSchristos 
64b985414bSchristos 	return error;
65b985414bSchristos }
66b985414bSchristos 
67b985414bSchristos static const struct statvfs *
find_statfs(const struct statvfs * mntbuf,int nitems,const char * mountpoint)68b985414bSchristos find_statfs(const struct statvfs *mntbuf, int nitems, const char *mountpoint)
69b985414bSchristos {
70b985414bSchristos 	int i;
71b985414bSchristos 
72b985414bSchristos 	for (i = 0; i < nitems; i++) {
73b985414bSchristos 		if (strcmp(mntbuf[i].f_mntonname, mountpoint) == 0)
74b985414bSchristos 			return mntbuf + i;
75b985414bSchristos 	}
76b985414bSchristos 
77b985414bSchristos 	return NULL;
78b985414bSchristos }
79b985414bSchristos 
80b985414bSchristos static void
mount_autofs(const char * from,const char * fspath,const char * options,const char * prefix)81b985414bSchristos mount_autofs(const char *from, const char *fspath, const char *options,
82b985414bSchristos     const char *prefix)
83b985414bSchristos {
84b985414bSchristos 	struct autofs_args args;
85b985414bSchristos 	int error;
86b985414bSchristos 
87b985414bSchristos 	create_directory(fspath);
88b985414bSchristos 
89b985414bSchristos 	log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"",
90b985414bSchristos 	    from, fspath, prefix, options);
91b985414bSchristos 
92b985414bSchristos 	memset(&args, 0, sizeof(args));
93651232daSchristos 	args.from = __UNCONST(from);
94651232daSchristos 	args.master_options = __UNCONST(options);
95651232daSchristos 	args.master_prefix = __UNCONST(prefix);
96b985414bSchristos 
97b985414bSchristos 	error = mount("autofs", fspath, 0, &args, sizeof(args));
98b985414bSchristos 	if (error != 0)
99b985414bSchristos 		log_err(1, "cannot mount %s on %s", from, fspath);
100b985414bSchristos }
101b985414bSchristos 
102b985414bSchristos static void
mount_if_not_already(const struct node * n,const char * map,const char * options,const char * prefix,const struct statvfs * mntbuf,int nitems)103b985414bSchristos mount_if_not_already(const struct node *n, const char *map, const char *options,
104b985414bSchristos     const char *prefix, const struct statvfs *mntbuf, int nitems)
105b985414bSchristos {
106b985414bSchristos 	const struct statvfs *sb;
107b985414bSchristos 	char *mountpoint;
108b985414bSchristos 	char *from;
109b985414bSchristos 	int ret;
110b985414bSchristos 
111b985414bSchristos 	ret = asprintf(&from, "map %s", map);
112b985414bSchristos 	if (ret < 0)
113b985414bSchristos 		log_err(1, "asprintf");
114b985414bSchristos 
115b985414bSchristos 	mountpoint = node_path(n);
116b985414bSchristos 	sb = find_statfs(mntbuf, nitems, mountpoint);
117b985414bSchristos 	if (sb != NULL) {
118b985414bSchristos 		if (strcmp(sb->f_fstypename, "autofs") != 0) {
119b985414bSchristos 			log_debugx("unknown filesystem mounted "
120b985414bSchristos 			    "on %s; mounting", mountpoint);
121b985414bSchristos 			/*
122b985414bSchristos 			 * XXX: Compare options and 'from',
123b985414bSchristos 			 *	and update the mount if necessary.
124b985414bSchristos 			 */
125b985414bSchristos 		} else {
126b985414bSchristos 			log_debugx("autofs already mounted "
127b985414bSchristos 			    "on %s", mountpoint);
128b985414bSchristos 			free(from);
129b985414bSchristos 			free(mountpoint);
130b985414bSchristos 			return;
131b985414bSchristos 		}
132b985414bSchristos 	} else {
133b985414bSchristos 		log_debugx("nothing mounted on %s; mounting",
134b985414bSchristos 		    mountpoint);
135b985414bSchristos 	}
136b985414bSchristos 
137b985414bSchristos 	mount_autofs(from, mountpoint, options, prefix);
138b985414bSchristos 	free(from);
139b985414bSchristos 	free(mountpoint);
140b985414bSchristos }
141b985414bSchristos 
142b985414bSchristos static void
mount_unmount(struct node * root)143b985414bSchristos mount_unmount(struct node *root)
144b985414bSchristos {
145b985414bSchristos 	struct statvfs *mntbuf;
146b985414bSchristos 	struct node *n, *n2;
147b985414bSchristos 	int i, nitems;
148243229afSchristos 	static char rootdir[] = "/";
149b985414bSchristos 
150b985414bSchristos 	nitems = getmntinfo(&mntbuf, MNT_WAIT);
151b985414bSchristos 	if (nitems <= 0)
152b985414bSchristos 		log_err(1, "getmntinfo");
153b985414bSchristos 
154b985414bSchristos 	log_debugx("unmounting stale autofs mounts");
155b985414bSchristos 
156b985414bSchristos 	for (i = 0; i < nitems; i++) {
157b985414bSchristos 		if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
158b985414bSchristos 			log_debugx("skipping %s, filesystem type is not autofs",
159b985414bSchristos 			    mntbuf[i].f_mntonname);
160b985414bSchristos 			continue;
161b985414bSchristos 		}
162b985414bSchristos 
163b985414bSchristos 		n = node_find(root, mntbuf[i].f_mntonname);
164b985414bSchristos 		if (n != NULL) {
165b985414bSchristos 			log_debugx("leaving autofs mounted on %s",
166b985414bSchristos 			    mntbuf[i].f_mntonname);
167b985414bSchristos 			continue;
168b985414bSchristos 		}
169b985414bSchristos 
170b985414bSchristos 		log_debugx("autofs mounted on %s not found "
171b985414bSchristos 		    "in new configuration; unmounting", mntbuf[i].f_mntonname);
172b985414bSchristos 		unmount_by_statfs(&(mntbuf[i]), false);
173b985414bSchristos 	}
174b985414bSchristos 
175b985414bSchristos 	log_debugx("mounting new autofs mounts");
176b985414bSchristos 
177b985414bSchristos 	TAILQ_FOREACH(n, &root->n_children, n_next) {
178b985414bSchristos 		if (!node_is_direct_map(n)) {
179b985414bSchristos 			mount_if_not_already(n, n->n_map, n->n_options,
180b985414bSchristos 			    n->n_key, mntbuf, nitems);
181b985414bSchristos 			continue;
182b985414bSchristos 		}
183b985414bSchristos 
184b985414bSchristos 		TAILQ_FOREACH(n2, &n->n_children, n_next) {
185b985414bSchristos 			mount_if_not_already(n2, n->n_map, n->n_options,
186243229afSchristos 			    rootdir, mntbuf, nitems);
187b985414bSchristos 		}
188b985414bSchristos 	}
189b985414bSchristos }
190b985414bSchristos 
191b985414bSchristos static void
flush_autofs(const char * fspath)192b985414bSchristos flush_autofs(const char *fspath)
193b985414bSchristos {
194b985414bSchristos 	int error;
195b985414bSchristos 
196b985414bSchristos 	log_debugx("flushing %s", fspath);
197b985414bSchristos 
198b985414bSchristos 	error = mount("autofs", fspath, MNT_UPDATE, NULL, 0);
199b985414bSchristos 	if (error != 0)
200b985414bSchristos 		log_err(1, "cannot flush %s", fspath);
201b985414bSchristos }
202b985414bSchristos 
203b985414bSchristos static void
flush_caches(void)204b985414bSchristos flush_caches(void)
205b985414bSchristos {
206b985414bSchristos 	struct statvfs *mntbuf;
207b985414bSchristos 	int i, nitems;
208b985414bSchristos 
209b985414bSchristos 	nitems = getmntinfo(&mntbuf, MNT_WAIT);
210b985414bSchristos 	if (nitems <= 0)
211b985414bSchristos 		log_err(1, "getmntinfo");
212b985414bSchristos 
213b985414bSchristos 	log_debugx("flushing autofs caches");
214b985414bSchristos 
215b985414bSchristos 	for (i = 0; i < nitems; i++) {
216b985414bSchristos 		if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
217b985414bSchristos 			log_debugx("skipping %s, filesystem type is not autofs",
218b985414bSchristos 			    mntbuf[i].f_mntonname);
219b985414bSchristos 			continue;
220b985414bSchristos 		}
221b985414bSchristos 
222b985414bSchristos 		flush_autofs(mntbuf[i].f_mntonname);
223b985414bSchristos 	}
224b985414bSchristos }
225b985414bSchristos 
226b985414bSchristos static void
unmount_automounted(bool force)227b985414bSchristos unmount_automounted(bool force)
228b985414bSchristos {
229b985414bSchristos 	struct statvfs *mntbuf;
230b985414bSchristos 	int i, nitems;
231b985414bSchristos 
232b985414bSchristos 	nitems = getmntinfo(&mntbuf, MNT_WAIT);
233b985414bSchristos 	if (nitems <= 0)
234b985414bSchristos 		log_err(1, "getmntinfo");
235b985414bSchristos 
236b985414bSchristos 	log_debugx("unmounting automounted filesystems");
237b985414bSchristos 
238b985414bSchristos 	for (i = 0; i < nitems; i++) {
239b985414bSchristos 		if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) {
240b985414bSchristos 			log_debugx("skipping %s, filesystem type is autofs",
241b985414bSchristos 			    mntbuf[i].f_mntonname);
242b985414bSchristos 			continue;
243b985414bSchristos 		}
244b985414bSchristos 
245b985414bSchristos 		if ((mntbuf[i].f_flag & MNT_AUTOMOUNTED) == 0) {
246b985414bSchristos 			log_debugx("skipping %s, not automounted",
247b985414bSchristos 			    mntbuf[i].f_mntonname);
248b985414bSchristos 			continue;
249b985414bSchristos 		}
250b985414bSchristos 
251b985414bSchristos 		unmount_by_statfs(&(mntbuf[i]), force);
252b985414bSchristos 	}
253b985414bSchristos }
254b985414bSchristos 
255b985414bSchristos __dead static void
usage_automount(void)256b985414bSchristos usage_automount(void)
257b985414bSchristos {
258b985414bSchristos 
259b985414bSchristos 	fprintf(stderr, "Usage: %s [-D name=value][-o opts][-Lcfuv]\n",
260b985414bSchristos 	    getprogname());
261b985414bSchristos 	exit(1);
262b985414bSchristos }
263b985414bSchristos 
264b985414bSchristos int
main_automount(int argc,char ** argv)265b985414bSchristos main_automount(int argc, char **argv)
266b985414bSchristos {
267b985414bSchristos 	struct node *root;
268b985414bSchristos 	int ch, debug = 0, show_maps = 0;
269b985414bSchristos 	char *options = NULL;
270b985414bSchristos 	bool do_unmount = false, force_unmount = false, flush = false;
271b985414bSchristos 
272b985414bSchristos 	/*
273b985414bSchristos 	 * Note that in automount(8), the only purpose of variable
274b985414bSchristos 	 * handling is to aid in debugging maps (automount -L).
275b985414bSchristos 	 */
276b985414bSchristos 	defined_init();
277b985414bSchristos 
278b985414bSchristos 	while ((ch = getopt(argc, argv, "D:Lfco:uv")) != -1) {
279b985414bSchristos 		switch (ch) {
280b985414bSchristos 		case 'D':
281b985414bSchristos 			defined_parse_and_add(optarg);
282b985414bSchristos 			break;
283b985414bSchristos 		case 'L':
284b985414bSchristos 			show_maps++;
285b985414bSchristos 			break;
286b985414bSchristos 		case 'c':
287b985414bSchristos 			flush = true;
288b985414bSchristos 			break;
289b985414bSchristos 		case 'f':
290b985414bSchristos 			force_unmount = true;
291b985414bSchristos 			break;
292b985414bSchristos 		case 'o':
293b985414bSchristos 			options = concat(options, ',', optarg);
294b985414bSchristos 			break;
295b985414bSchristos 		case 'u':
296b985414bSchristos 			do_unmount = true;
297b985414bSchristos 			break;
298b985414bSchristos 		case 'v':
299b985414bSchristos 			debug++;
300b985414bSchristos 			break;
301b985414bSchristos 		case '?':
302b985414bSchristos 		default:
303b985414bSchristos 			usage_automount();
304b985414bSchristos 		}
305b985414bSchristos 	}
306b985414bSchristos 	argc -= optind;
307b985414bSchristos 	if (argc != 0)
308b985414bSchristos 		usage_automount();
309b985414bSchristos 
310b985414bSchristos 	if (force_unmount && !do_unmount)
311b985414bSchristos 		usage_automount();
312b985414bSchristos 
313b985414bSchristos 	log_init(debug);
314b985414bSchristos 
315b985414bSchristos 	if (flush) {
316b985414bSchristos 		flush_caches();
317b985414bSchristos 		return 0;
318b985414bSchristos 	}
319b985414bSchristos 
320b985414bSchristos 	if (do_unmount) {
321b985414bSchristos 		unmount_automounted(force_unmount);
322b985414bSchristos 		return 0;
323b985414bSchristos 	}
324b985414bSchristos 
325b985414bSchristos 	root = node_new_root();
326b985414bSchristos 	parse_master(root, AUTO_MASTER_PATH);
327b985414bSchristos 
328b985414bSchristos 	if (show_maps) {
329b985414bSchristos 		if (show_maps > 1) {
330b985414bSchristos 			node_expand_indirect_maps(root);
331b985414bSchristos 			node_expand_ampersand(root, NULL);
332b985414bSchristos 		}
333b985414bSchristos 		node_expand_defined(root);
334b985414bSchristos 		node_print(root, options);
335b985414bSchristos 		return 0;
336b985414bSchristos 	}
337b985414bSchristos 
338b985414bSchristos 	mount_unmount(root);
339b985414bSchristos 
340b985414bSchristos 	return 0;
341b985414bSchristos }
342