1/*	$NetBSD: mount_lfs.c,v 1.39 2016/02/21 22:51:29 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1993, 1994
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34__COPYRIGHT("@(#) Copyright (c) 1993, 1994\
35 The Regents of the University of California.  All rights reserved.");
36#endif /* not lint */
37
38#ifndef lint
39#if 0
40static char sccsid[] = "@(#)mount_lfs.c	8.4 (Berkeley) 4/26/95";
41#else
42__RCSID("$NetBSD: mount_lfs.c,v 1.39 2016/02/21 22:51:29 christos Exp $");
43#endif
44#endif /* not lint */
45
46#include <sys/param.h>
47#include <sys/mount.h>
48
49#include <ufs/lfs/lfs.h>
50
51#include <err.h>
52#include <errno.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
56#include <unistd.h>
57#include <paths.h>
58
59#include <signal.h>
60
61#include <mntopts.h>
62#include "pathnames.h"
63#include "mountprog.h"
64#include "mount_lfs.h"
65
66static const struct mntopt mopts[] = {
67	MOPT_STDOPTS,
68	MOPT_UPDATE,
69	MOPT_GETARGS,
70	MOPT_NOATIME,
71	MOPT_RELATIME,
72	MOPT_NULL,
73};
74
75__dead static void	usage(void);
76
77#ifdef WANT_CLEANER
78__dead static void	invoke_cleaner(char *);
79static void	kill_daemon(char *);
80static void	kill_cleaner(char *);
81#endif /* WANT_CLEANER */
82
83static int short_rds, cleaner_debug, cleaner_bytes, fs_idle, noclean;
84static const char *nsegs;
85
86#ifndef MOUNT_NOMAIN
87int
88main(int argc, char **argv)
89{
90
91	setprogname(argv[0]);
92	return mount_lfs(argc, argv);
93}
94#endif
95
96void
97mount_lfs_parseargs(int argc, char *argv[],
98	struct ulfs_args *args, int *mntflags,
99	char *canon_dev, char *canon_dir)
100{
101	int ch;
102	mntoptparse_t mp;
103
104	memset(args, 0, sizeof(*args));
105	nsegs = "4";
106	*mntflags = noclean = 0;
107	cleaner_bytes = 1;
108	while ((ch = getopt(argc, argv, "bdiN:no:s")) != -1)
109		switch (ch) {
110		case 'b':
111			cleaner_bytes = !cleaner_bytes;
112			break;
113		case 'd':
114			cleaner_debug = 1;
115			break;
116		case 'i':
117			fs_idle = 1;
118			break;
119		case 'n':
120			noclean = 1;
121			break;
122		case 'N':
123			nsegs = optarg;
124			break;
125		case 'o':
126			mp = getmntopts(optarg, mopts, mntflags, 0);
127			if (mp == NULL)
128				err(1, "getmntopts");
129			freemntopts(mp);
130			break;
131		case 's':
132			short_rds = 1;
133			break;
134		case '?':
135		default:
136			usage();
137		}
138	argc -= optind;
139	argv += optind;
140
141	if (argc != 2)
142		usage();
143
144	pathadj(argv[0], canon_dev);
145	args->fspec = canon_dev;
146
147	pathadj(argv[1], canon_dir);
148}
149
150int
151mount_lfs(int argc, char *argv[])
152{
153	struct ulfs_args args;
154	int mntflags;
155	int mntsize, oldflags, i;
156	char fs_name[MAXPATHLEN], canon_dev[MAXPATHLEN];
157	struct statvfs *mntbuf;
158	const char *errcause;
159
160	mount_lfs_parseargs(argc, argv, &args, &mntflags, canon_dev, fs_name);
161
162	/*
163	 * Record the previous status of this filesystem (if any) before
164	 * performing the mount, so we can know whether to start or
165	 * kill the cleaner process below.
166	 */
167	oldflags = MNT_RDONLY; /* If not mounted, pretend r/o */
168	if (mntflags & MNT_UPDATE) {
169		if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
170			err(1, "getmntinfo");
171		for (i = 0; i < mntsize; i++) {
172			if (strcmp(mntbuf[i].f_mntfromname, args.fspec) == 0) {
173				oldflags = mntbuf[i].f_flag;
174				break;
175			}
176		}
177	}
178
179	if (mount(MOUNT_LFS, fs_name, mntflags, &args, sizeof args) == -1) {
180		switch (errno) {
181		case EMFILE:
182			errcause = "mount table full";
183			break;
184		case EINVAL:
185			if (mntflags & MNT_UPDATE)
186				errcause =
187			    "specified device does not match mounted device";
188			else
189				errcause = "incorrect super block";
190			break;
191		default:
192			errcause = strerror(errno);
193			break;
194		}
195		errx(1, "%s on %s: %s", args.fspec, fs_name, errcause);
196	}
197
198#ifdef WANT_CLEANER
199	/* Not mounting fresh or upgrading to r/w; don't start the cleaner */
200	if (!(oldflags & MNT_RDONLY) || (mntflags & MNT_RDONLY)
201	    || (mntflags & MNT_GETARGS))
202		noclean = 1;
203	if (!noclean)
204		invoke_cleaner(fs_name);
205		/* NOTREACHED */
206
207	/* Downgrade to r/o; kill the cleaner */
208	if ((mntflags & MNT_RDONLY) && !(oldflags & MNT_RDONLY))
209		kill_cleaner(fs_name);
210#else
211	__USE(oldflags);
212#endif /* WANT_CLEANER */
213
214	exit(0);
215}
216
217#ifdef WANT_CLEANER
218static void
219kill_daemon(char *pidname)
220{
221	FILE *fp;
222	char s[80];
223	pid_t pid;
224
225	fp = fopen(pidname, "r");
226	if (fp) {
227		fgets(s, 80, fp);
228		pid = atoi(s);
229		if (pid)
230			kill(pid, SIGINT);
231		fclose(fp);
232	}
233}
234
235static void
236kill_cleaner(char *name)
237{
238	char *pidname;
239	char *cp;
240	int off;
241
242	/* Parent first */
243	asprintf(&pidname, "%slfs_cleanerd:m:%s.pid", _PATH_VARRUN, name);
244	if (!pidname)
245		err(1, "malloc");
246	off = strlen(_PATH_VARRUN);
247	while((cp = strchr(pidname + off, '/')) != NULL)
248		*cp = '|';
249	kill_daemon(pidname);
250	free(pidname);
251
252	/* Then child */
253	asprintf(&pidname, "%slfs_cleanerd:s:%s.pid", _PATH_VARRUN, name);
254	if (!pidname)
255		err(1, "malloc");
256	off = strlen(_PATH_VARRUN);
257	while((cp = strchr(pidname + off, '/')) != NULL)
258		*cp = '|';
259	kill_daemon(pidname);
260	free(pidname);
261}
262
263static void
264invoke_cleaner(char *name)
265{
266	const char *args[7], **ap = args;
267
268	/* Build the argument list. */
269	*ap++ = _PATH_LFS_CLEANERD;
270	if (cleaner_bytes)
271		*ap++ = "-b";
272	if (nsegs) {
273		*ap++ = "-n";
274		*ap++ = nsegs;
275	}
276	if (short_rds)
277		*ap++ = "-s";
278	if (cleaner_debug)
279		*ap++ = "-d";
280	if (fs_idle)
281		*ap++ = "-f";
282	*ap++ = name;
283	*ap = NULL;
284
285	execv(args[0], __UNCONST(args));
286	err(1, "exec %s", _PATH_LFS_CLEANERD);
287}
288#endif /* WANT_CLEANER */
289
290static void
291usage(void)
292{
293	(void)fprintf(stderr,
294		"usage: %s [-bdins] [-N nsegs] [-o options] special node\n",
295		getprogname());
296	exit(1);
297}
298