fsck.c revision 70522
1/*	$NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $	*/
2
3/*
4 * Copyright (c) 1996 Christos Zoulas. All rights reserved.
5 * Copyright (c) 1980, 1989, 1993, 1994
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * From: @(#)mount.c	8.19 (Berkeley) 4/19/94
37 * From: $NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp
38 * $NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $
39 */
40
41#include <sys/cdefs.h>
42#ifndef lint
43__RCSID("$FreeBSD: head/sbin/fsck/fsck.c 70522 2000-12-30 21:05:45Z phk $");
44#endif /* not lint */
45
46#include <sys/param.h>
47#include <sys/mount.h>
48#include <sys/queue.h>
49#include <sys/wait.h>
50#define FSTYPENAMES
51#define DKTYPENAMES
52#include <sys/disklabel.h>
53#include <sys/ioctl.h>
54
55#include <err.h>
56#include <errno.h>
57#include <fstab.h>
58#include <fcntl.h>
59#include <paths.h>
60#include <signal.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include <unistd.h>
65
66#include "pathnames.h"
67#include "fsutil.h"
68
69static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
70
71TAILQ_HEAD(fstypelist, entry) opthead, selhead;
72
73struct entry {
74	char *type;
75	char *options;
76	TAILQ_ENTRY(entry) entries;
77};
78
79static int maxrun = 0;
80static char *options = NULL;
81static int flags = 0;
82
83int main __P((int, char *[]));
84
85static int checkfs __P((const char *, const char *, const char *, void *,
86    pid_t *));
87static int selected __P((const char *));
88static void addoption __P((char *));
89static const char *getoptions __P((const char *));
90static void addentry __P((struct fstypelist *, const char *, const char *));
91static void maketypelist __P((char *));
92static void catopt __P((char **, const char *));
93static void mangle __P((char *, int *, const char ***, int *));
94static const char *getfslab __P((const char *));
95static void usage __P((void));
96static void *isok __P((struct fstab *));
97
98int
99main(argc, argv)
100	int argc;
101	char *argv[];
102{
103	struct fstab *fs;
104	int i, rval = 0;
105	const char *vfstype = NULL;
106	char globopt[3];
107
108	globopt[0] = '-';
109	globopt[2] = '\0';
110
111	TAILQ_INIT(&selhead);
112	TAILQ_INIT(&opthead);
113
114	while ((i = getopt(argc, argv, "dvpfnyl:t:T:")) != -1)
115		switch (i) {
116		case 'd':
117			flags |= CHECK_DEBUG;
118			break;
119
120		case 'v':
121			flags |= CHECK_VERBOSE;
122			break;
123
124		case 'p':
125			flags |= CHECK_PREEN;
126			/*FALLTHROUGH*/
127		case 'n':
128		case 'f':
129		case 'y':
130			globopt[1] = i;
131			catopt(&options, globopt);
132			break;
133
134		case 'l':
135			maxrun = atoi(optarg);
136			break;
137
138		case 'T':
139			if (*optarg)
140				addoption(optarg);
141			break;
142
143		case 't':
144			if (!TAILQ_EMPTY(&selhead))
145				errx(1, "only one -t option may be specified.");
146
147			maketypelist(optarg);
148			vfstype = optarg;
149			break;
150
151		case '?':
152		default:
153			usage();
154			/* NOTREACHED */
155		}
156
157	argc -= optind;
158	argv += optind;
159
160	if (argc == 0)
161		return checkfstab(flags, maxrun, isok, checkfs);
162
163#define	BADTYPE(type)							\
164	(strcmp(type, FSTAB_RO) &&					\
165	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
166
167
168	for (; argc--; argv++) {
169		const char *spec, *type, *cp;
170		char	device[MAXPATHLEN];
171
172		spec = *argv;
173		cp = strrchr(spec, '/');
174		if (cp == 0) {
175			(void)snprintf(device, sizeof(device), "%s%s",
176				_PATH_DEV, spec);
177			spec = device;
178		}
179		if ((fs = getfsfile(spec)) == NULL &&
180		    (fs = getfsspec(spec)) == NULL) {
181			if (vfstype == NULL)
182				vfstype = getfslab(spec);
183			type = vfstype;
184		}
185		else {
186			spec = fs->fs_spec;
187			type = fs->fs_vfstype;
188			if (BADTYPE(fs->fs_type))
189				errx(1, "%s has unknown file system type.",
190				    spec);
191		}
192
193		rval |= checkfs(type, devcheck(spec), *argv, NULL, NULL);
194	}
195
196	return rval;
197}
198
199
200static void *
201isok(fs)
202	struct fstab *fs;
203{
204	if (fs->fs_passno == 0)
205		return NULL;
206
207	if (BADTYPE(fs->fs_type))
208		return NULL;
209
210	if (!selected(fs->fs_vfstype))
211		return NULL;
212
213	return fs;
214}
215
216
217static int
218checkfs(pvfstype, spec, mntpt, auxarg, pidp)
219	const char *pvfstype, *spec, *mntpt;
220	void *auxarg;
221	pid_t *pidp;
222{
223	/* List of directories containing fsck_xxx subcommands. */
224	static const char *edirs[] = {
225		_PATH_SBIN,
226		_PATH_USRSBIN,
227		NULL
228	};
229	const char **argv, **edir;
230	pid_t pid;
231	int argc, i, status, maxargc;
232	char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
233	char *vfstype = NULL;
234	const char *extra = NULL;
235
236#ifdef __GNUC__
237	/* Avoid vfork clobbering */
238	(void) &optbuf;
239	(void) &vfstype;
240#endif
241	/*
242	 * We convert the vfstype to lowercase and any spaces to underscores
243	 * to not confuse the issue
244	 */
245	vfstype = strdup(pvfstype);
246	if (vfstype == NULL)
247		perror("strdup(pvfstype)");
248	for (i = 0; i < strlen(vfstype); i++) {
249		vfstype[i] = tolower(vfstype[i]);
250		if (vfstype[i] == ' ')
251			vfstype[i] = '_';
252	}
253
254	extra = getoptions(vfstype);
255	optbuf = NULL;
256	if (options)
257		catopt(&optbuf, options);
258	if (extra)
259		catopt(&optbuf, extra);
260
261	maxargc = 64;
262	argv = emalloc(sizeof(char *) * maxargc);
263
264	(void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
265	argc = 0;
266	argv[argc++] = execbase;
267	if (optbuf)
268		mangle(optbuf, &argc, &argv, &maxargc);
269	argv[argc++] = spec;
270	argv[argc] = NULL;
271
272	if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
273		(void)printf("start %s %swait", mntpt,
274			pidp ? "no" : "");
275		for (i = 0; i < argc; i++)
276			(void)printf(" %s", argv[i]);
277		(void)printf("\n");
278	}
279
280	switch (pid = vfork()) {
281	case -1:				/* Error. */
282		warn("vfork");
283		if (optbuf)
284			free(optbuf);
285		free(vfstype);
286		return (1);
287
288	case 0:					/* Child. */
289		if (flags & CHECK_DEBUG)
290			_exit(0);
291
292		/* Go find an executable. */
293		edir = edirs;
294		do {
295			(void)snprintf(execname,
296			    sizeof(execname), "%s/%s", *edir, execbase);
297			execv(execname, (char * const *)argv);
298			if (errno != ENOENT) {
299				if (spec)
300					warn("exec %s for %s", execname, spec);
301				else
302					warn("exec %s", execname);
303			}
304		} while (*++edir != NULL);
305
306		if (errno == ENOENT) {
307			if (spec)
308				warn("exec %s for %s", execname, spec);
309			else
310				warn("exec %s", execname);
311		}
312		_exit(1);
313		/* NOTREACHED */
314
315	default:				/* Parent. */
316		if (optbuf)
317			free(optbuf);
318
319		free(vfstype);
320
321		if (pidp) {
322			*pidp = pid;
323			return 0;
324		}
325
326		if (waitpid(pid, &status, 0) < 0) {
327			warn("waitpid");
328			return (1);
329		}
330
331		if (WIFEXITED(status)) {
332			if (WEXITSTATUS(status) != 0)
333				return (WEXITSTATUS(status));
334		}
335		else if (WIFSIGNALED(status)) {
336			warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
337			return (1);
338		}
339		break;
340	}
341
342	return (0);
343}
344
345
346static int
347selected(type)
348	const char *type;
349{
350	struct entry *e;
351
352	/* If no type specified, it's always selected. */
353	TAILQ_FOREACH(e, &selhead, entries)
354		if (!strncmp(e->type, type, MFSNAMELEN))
355			return which == IN_LIST ? 1 : 0;
356
357	return which == IN_LIST ? 0 : 1;
358}
359
360
361static const char *
362getoptions(type)
363	const char *type;
364{
365	struct entry *e;
366
367	TAILQ_FOREACH(e, &opthead, entries)
368		if (!strncmp(e->type, type, MFSNAMELEN))
369			return e->options;
370	return "";
371}
372
373
374static void
375addoption(optstr)
376	char *optstr;
377{
378	char *newoptions;
379	struct entry *e;
380
381	if ((newoptions = strchr(optstr, ':')) == NULL)
382		errx(1, "Invalid option string");
383
384	*newoptions++ = '\0';
385
386	TAILQ_FOREACH(e, &opthead, entries)
387		if (!strncmp(e->type, optstr, MFSNAMELEN)) {
388			catopt(&e->options, newoptions);
389			return;
390		}
391	addentry(&opthead, optstr, newoptions);
392}
393
394
395static void
396addentry(list, type, opts)
397	struct fstypelist *list;
398	const char *type;
399	const char *opts;
400{
401	struct entry *e;
402
403	e = emalloc(sizeof(struct entry));
404	e->type = estrdup(type);
405	e->options = estrdup(opts);
406	TAILQ_INSERT_TAIL(list, e, entries);
407}
408
409
410static void
411maketypelist(fslist)
412	char *fslist;
413{
414	char *ptr;
415
416	if ((fslist == NULL) || (fslist[0] == '\0'))
417		errx(1, "empty type list");
418
419	if (fslist[0] == 'n' && fslist[1] == 'o') {
420		fslist += 2;
421		which = NOT_IN_LIST;
422	}
423	else
424		which = IN_LIST;
425
426	while ((ptr = strsep(&fslist, ",")) != NULL)
427		addentry(&selhead, ptr, "");
428
429}
430
431
432static void
433catopt(sp, o)
434	char **sp;
435	const char *o;
436{
437	char *s;
438	size_t i, j;
439
440	s = *sp;
441	if (s) {
442		i = strlen(s);
443		j = i + 1 + strlen(o) + 1;
444		s = erealloc(s, j);
445		(void)snprintf(s + i, j, ",%s", o);
446	} else
447		s = estrdup(o);
448	*sp = s;
449}
450
451
452static void
453mangle(options, argcp, argvp, maxargcp)
454	char *options;
455	int *argcp, *maxargcp;
456	const char ***argvp;
457{
458	char *p, *s;
459	int argc, maxargc;
460	const char **argv;
461
462	argc = *argcp;
463	argv = *argvp;
464	maxargc = *maxargcp;
465
466	for (s = options; (p = strsep(&s, ",")) != NULL;) {
467		/* Always leave space for one more argument and the NULL. */
468		if (argc >= maxargc - 3) {
469			maxargc <<= 1;
470			argv = erealloc(argv, maxargc * sizeof(char *));
471		}
472		if (*p != '\0')  {
473			if (*p == '-') {
474				argv[argc++] = p;
475				p = strchr(p, '=');
476				if (p) {
477					*p = '\0';
478					argv[argc++] = p+1;
479				}
480			} else {
481				argv[argc++] = "-o";
482				argv[argc++] = p;
483			}
484		}
485	}
486
487	*argcp = argc;
488	*argvp = argv;
489	*maxargcp = maxargc;
490}
491
492
493const static char *
494getfslab(str)
495	const char *str;
496{
497	struct disklabel dl;
498	int fd;
499	char p;
500	const char *vfstype;
501	u_char t;
502
503	/* deduce the filesystem type from the disk label */
504	if ((fd = open(str, O_RDONLY)) == -1)
505		err(1, "cannot open `%s'", str);
506
507	if (ioctl(fd, DIOCGDINFO, &dl) == -1)
508		err(1, "cannot get disklabel for `%s'", str);
509
510	(void) close(fd);
511
512	p = str[strlen(str) - 1];
513
514	if ((p - 'a') >= dl.d_npartitions)
515		errx(1, "partition `%s' is not defined on disk", str);
516
517	if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES)
518		errx(1, "partition `%s' is not of a legal vfstype",
519		    str);
520
521	if ((vfstype = fstypenames[t]) == NULL)
522		errx(1, "vfstype `%s' on partition `%s' is not supported",
523		    fstypenames[t], str);
524
525	return vfstype;
526}
527
528
529static void
530usage()
531{
532	extern char *__progname;
533	static const char common[] =
534	    "[-dpvlyn] [-T fstype:fsoptions] [-t fstype]";
535
536	(void)fprintf(stderr, "Usage: %s %s [special|node]...\n",
537	    __progname, common);
538	exit(1);
539}
540