snj doesn't like my accent, so use proper English month names.
[netbsd-mini2440.git] / sbin / fsck / fsck.c
blob528eafe02410ed0239f007cef334b44fe5c00a9f
1 /* $NetBSD: fsck.c,v 1.46 2007/07/17 20:12:40 christos Exp $ */
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.
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. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
32 * From: @(#)mount.c 8.19 (Berkeley) 4/19/94
33 * From: NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp
37 #include <sys/cdefs.h>
38 #ifndef lint
39 __RCSID("$NetBSD: fsck.c,v 1.46 2007/07/17 20:12:40 christos Exp $");
40 #endif /* not lint */
42 #include <sys/param.h>
43 #include <sys/mount.h>
44 #include <sys/queue.h>
45 #include <sys/wait.h>
46 #define FSTYPENAMES
47 #define FSCKNAMES
48 #include <sys/disk.h>
49 #include <sys/disklabel.h>
50 #include <sys/ioctl.h>
52 #include <err.h>
53 #include <errno.h>
54 #include <fstab.h>
55 #include <fcntl.h>
56 #include <paths.h>
57 #include <signal.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <util.h>
64 #include "pathnames.h"
65 #include "fsutil.h"
66 #include "exitvalues.h"
68 static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
70 TAILQ_HEAD(fstypelist, entry) opthead, selhead;
72 struct entry {
73 char *type;
74 char *options;
75 TAILQ_ENTRY(entry) entries;
78 static int maxrun = 0;
79 static char *options = NULL;
80 static int flags = 0;
82 static int checkfs(const char *, const char *, const char *, void *, pid_t *);
83 static int selected(const char *);
84 static void addoption(char *);
85 static const char *getoptions(const char *);
86 static void addentry(struct fstypelist *, const char *, const char *);
87 static void maketypelist(char *);
88 static void catopt(char **, const char *);
89 static void mangle(char *, int *, const char ** volatile *, int *);
90 static const char *getfslab(const char *);
91 static void usage(void);
92 static void *isok(struct fstab *);
94 int
95 main(int argc, char *argv[])
97 struct fstab *fs;
98 int i, rval;
99 const char *vfstype = NULL;
100 char globopt[3];
101 int ret = FSCK_EXIT_OK;
103 globopt[0] = '-';
104 globopt[2] = '\0';
106 TAILQ_INIT(&selhead);
107 TAILQ_INIT(&opthead);
109 while ((i = getopt(argc, argv, "dfl:nPpqT:t:vy")) != -1) {
110 switch (i) {
111 case 'd':
112 flags |= CHECK_DEBUG;
113 continue;
115 case 'f':
116 flags |= CHECK_FORCE;
117 break;
119 case 'n':
120 flags |= CHECK_NOFIX;
121 break;
123 case 'p':
124 flags |= CHECK_PREEN;
125 break;
127 case 'P':
128 flags |= CHECK_PROGRESS;
129 break;
131 case 'q':
132 break;
134 case 'l':
135 maxrun = atoi(optarg);
136 continue;
138 case 'T':
139 if (*optarg)
140 addoption(optarg);
141 continue;
143 case 't':
144 if (TAILQ_FIRST(&selhead) != NULL)
145 errx(1, "only one -t option may be specified.");
147 maketypelist(optarg);
148 vfstype = optarg;
149 continue;
151 case 'v':
152 flags |= CHECK_VERBOSE;
153 continue;
155 case 'y':
156 break;
158 case '?':
159 default:
160 usage();
161 /* NOTREACHED */
164 /* Pass option to fsck_xxxfs */
165 globopt[1] = i;
166 catopt(&options, globopt);
169 /* Don't do progress meters if we're debugging. */
170 if (flags & CHECK_DEBUG)
171 flags &= ~CHECK_PROGRESS;
174 * If progress meters are being used, force max parallel to 1
175 * so the progress meter outputs don't interfere with one another.
177 if (flags & CHECK_PROGRESS)
178 maxrun = 1;
180 argc -= optind;
181 argv += optind;
183 if (argc == 0)
184 return checkfstab(flags, maxrun, isok, checkfs);
186 #define BADTYPE(type) \
187 (strcmp(type, FSTAB_RO) && \
188 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
191 for (; argc--; argv++) {
192 const char *spec, *type, *cp;
193 char device[MAXPATHLEN];
195 spec = *argv;
196 cp = strrchr(spec, '/');
197 if (cp == 0) {
198 (void)snprintf(device, sizeof(device), "%s%s",
199 _PATH_DEV, spec);
200 spec = device;
202 if ((fs = getfsfile(spec)) == NULL &&
203 (fs = getfsspec(spec)) == NULL) {
204 if (vfstype == NULL)
205 vfstype = getfslab(spec);
206 type = vfstype;
208 else {
209 spec = fs->fs_spec;
210 type = fs->fs_vfstype;
211 if (BADTYPE(fs->fs_type))
212 errx(FSCK_EXIT_CHECK_FAILED,
213 "%s has unknown file system type.",
214 spec);
217 rval = checkfs(type, blockcheck(spec), *argv, NULL, NULL);
218 if (rval > ret)
219 ret = rval;
222 return ret;
226 static void *
227 isok(struct fstab *fs)
230 if (fs->fs_passno == 0)
231 return NULL;
233 if (BADTYPE(fs->fs_type))
234 return NULL;
236 if (!selected(fs->fs_vfstype))
237 return NULL;
239 return fs;
243 static int
244 checkfs(const char *vfst, const char *spec, const char *mntpt, void *auxarg,
245 pid_t *pidp)
247 /* List of directories containing fsck_xxx subcommands. */
248 static const char *edirs[] = {
249 #ifdef RESCUEDIR
250 RESCUEDIR,
251 #endif
252 _PATH_SBIN,
253 _PATH_USRSBIN,
254 NULL
256 const char ** volatile argv, **edir;
257 const char * volatile vfstype = vfst;
258 pid_t pid;
259 int argc, i, status, maxargc;
260 char *optb;
261 char *volatile optbuf;
262 char execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
263 const char *extra = getoptions(vfstype);
265 if (!strcmp(vfstype, "ufs"))
266 vfstype = MOUNT_UFS;
268 optb = NULL;
269 if (options)
270 catopt(&optb, options);
271 if (extra)
272 catopt(&optb, extra);
273 optbuf = optb;
275 maxargc = 64;
276 argv = emalloc(sizeof(char *) * maxargc);
278 (void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
279 argc = 0;
280 argv[argc++] = execbase;
281 if (optbuf)
282 mangle(optbuf, &argc, &argv, &maxargc);
283 argv[argc++] = spec;
284 argv[argc] = NULL;
286 if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
287 (void)printf("start %s %swait", mntpt,
288 pidp ? "no" : "");
289 for (i = 0; i < argc; i++)
290 (void)printf(" %s", argv[i]);
291 (void)printf("\n");
294 switch (pid = vfork()) {
295 case -1: /* Error. */
296 warn("vfork");
297 if (optbuf)
298 free(optbuf);
299 free(argv);
300 return FSCK_EXIT_CHECK_FAILED;
302 case 0: /* Child. */
303 if ((flags & CHECK_FORCE) == 0) {
304 struct statvfs sfs;
307 * if mntpt is a mountpoint of a mounted file
308 * system and it's mounted read-write, skip it
309 * unless -f is given.
311 if ((statvfs(mntpt, &sfs) == 0) &&
312 (strcmp(mntpt, sfs.f_mntonname) == 0) &&
313 ((sfs.f_flag & MNT_RDONLY) == 0)) {
314 printf(
315 "%s: file system is mounted read-write on %s; not checking\n",
316 spec, mntpt);
317 if ((flags & CHECK_PREEN) && auxarg != NULL)
318 _exit(FSCK_EXIT_OK); /* fsck -p */
319 else
320 _exit(FSCK_EXIT_CHECK_FAILED); /* fsck [[-p] ...] */
324 if (flags & CHECK_DEBUG)
325 _exit(FSCK_EXIT_OK);
327 /* Go find an executable. */
328 edir = edirs;
329 do {
330 (void)snprintf(execname,
331 sizeof(execname), "%s/%s", *edir, execbase);
332 execv(execname, (char * const *)__UNCONST(argv));
333 if (errno != ENOENT) {
334 if (spec)
335 warn("exec %s for %s", execname, spec);
336 else
337 warn("exec %s", execname);
339 } while (*++edir != NULL);
341 if (errno == ENOENT) {
342 if (spec)
343 warn("exec %s for %s", execname, spec);
344 else
345 warn("exec %s", execname);
347 _exit(FSCK_EXIT_CHECK_FAILED);
348 /* NOTREACHED */
350 default: /* Parent. */
351 if (optbuf)
352 free(optbuf);
353 free(argv);
355 if (pidp) {
356 *pidp = pid;
357 return FSCK_EXIT_OK;
360 if (waitpid(pid, &status, 0) < 0) {
361 warn("waitpid");
362 return FSCK_EXIT_CHECK_FAILED;
365 if (WIFEXITED(status)) {
366 if (WEXITSTATUS(status) != 0)
367 return WEXITSTATUS(status);
369 else if (WIFSIGNALED(status)) {
370 warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
371 return FSCK_EXIT_CHECK_FAILED;
373 break;
376 return FSCK_EXIT_OK;
380 static int
381 selected(const char *type)
383 struct entry *e;
385 /* If no type specified, it's always selected. */
386 TAILQ_FOREACH(e, &selhead, entries)
387 if (!strcmp(e->type, type))
388 return which == IN_LIST ? 1 : 0;
390 return which == IN_LIST ? 0 : 1;
394 static const char *
395 getoptions(const char *type)
397 struct entry *e;
399 TAILQ_FOREACH(e, &opthead, entries)
400 if (!strcmp(e->type, type))
401 return e->options;
402 return "";
406 static void
407 addoption(char *optstr)
409 char *newoptions;
410 struct entry *e;
412 if ((newoptions = strchr(optstr, ':')) == NULL)
413 errx(1, "Invalid option string");
415 *newoptions++ = '\0';
417 TAILQ_FOREACH(e, &opthead, entries)
418 if (!strcmp(e->type, optstr)) {
419 catopt(&e->options, newoptions);
420 return;
422 addentry(&opthead, optstr, newoptions);
426 static void
427 addentry(struct fstypelist *list, const char *type, const char *opts)
429 struct entry *e;
431 e = emalloc(sizeof(struct entry));
432 e->type = estrdup(type);
433 e->options = estrdup(opts);
434 TAILQ_INSERT_TAIL(list, e, entries);
438 static void
439 maketypelist(char *fslist)
441 char *ptr;
443 if ((fslist == NULL) || (fslist[0] == '\0'))
444 errx(1, "empty type list");
446 if (fslist[0] == 'n' && fslist[1] == 'o') {
447 fslist += 2;
448 which = NOT_IN_LIST;
450 else
451 which = IN_LIST;
453 while ((ptr = strsep(&fslist, ",")) != NULL)
454 addentry(&selhead, ptr, "");
459 static void
460 catopt(char **sp, const char *o)
462 char *s;
463 size_t i, j;
465 s = *sp;
466 if (s) {
467 i = strlen(s);
468 j = i + 1 + strlen(o) + 1;
469 s = erealloc(s, j);
470 (void)snprintf(s + i, j, ",%s", o);
471 } else
472 s = estrdup(o);
473 *sp = s;
477 static void
478 mangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
480 char *p, *s;
481 int argc, maxargc;
482 const char **argv;
484 argc = *argcp;
485 argv = *argvp;
486 maxargc = *maxargcp;
488 for (s = opts; (p = strsep(&s, ",")) != NULL;) {
489 /* Always leave space for one more argument and the NULL. */
490 if (argc >= maxargc - 3) {
491 maxargc <<= 1;
492 argv = erealloc(argv, maxargc * sizeof(char *));
494 if (*p != '\0') {
495 if (*p == '-') {
496 argv[argc++] = p;
497 p = strchr(p, '=');
498 if (p) {
499 *p = '\0';
500 argv[argc++] = p+1;
502 } else {
503 argv[argc++] = "-o";
504 argv[argc++] = p;
509 *argcp = argc;
510 *argvp = argv;
511 *maxargcp = maxargc;
514 static const char *
515 getfslab(const char *str)
517 static struct dkwedge_info dkw;
518 struct disklabel dl;
519 int fd;
520 char p;
521 const char *vfstype;
522 u_char t;
524 /* deduce the file system type from the disk label */
525 if ((fd = open(str, O_RDONLY)) == -1)
526 err(1, "cannot open `%s'", str);
528 /* First check to see if it's a wedge. */
529 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
530 /* Yup, this is easy. */
531 (void) close(fd);
532 return (dkw.dkw_ptype);
535 if (ioctl(fd, DIOCGDINFO, &dl) == -1)
536 err(1, "cannot get disklabel for `%s'", str);
538 (void) close(fd);
540 p = str[strlen(str) - 1];
542 if ((p - 'a') >= dl.d_npartitions)
543 errx(1, "partition `%s' is not defined on disk", str);
545 if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES)
546 errx(1, "partition `%s' is not of a legal vfstype",
547 str);
549 if ((vfstype = fscknames[t]) == NULL)
550 errx(1, "vfstype `%s' on partition `%s' is not supported",
551 fstypenames[t], str);
553 return vfstype;
557 static void
558 usage(void)
560 static const char common[] =
561 "[-dfnPpqvy] [-l maxparallel] [-T fstype:fsoptions]\n\t\t[-t fstype]";
563 (void)fprintf(stderr, "usage: %s %s [special|node]...\n",
564 getprogname(), common);
565 exit(FSCK_EXIT_USAGE);