fsck.c revision 1.44 1 /* $NetBSD: fsck.c,v 1.44 2006/10/16 02:44:46 christos 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. 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.
19 *
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.
31 *
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
34 *
35 */
36
37 #include <sys/cdefs.h>
38 #ifndef lint
39 __RCSID("$NetBSD: fsck.c,v 1.44 2006/10/16 02:44:46 christos Exp $");
40 #endif /* not lint */
41
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>
51
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>
63
64 #include "pathnames.h"
65 #include "fsutil.h"
66
67 static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
68
69 TAILQ_HEAD(fstypelist, entry) opthead, selhead;
70
71 struct entry {
72 char *type;
73 char *options;
74 TAILQ_ENTRY(entry) entries;
75 };
76
77 static int maxrun = 0;
78 static char *options = NULL;
79 static int flags = 0;
80
81 static int checkfs(const char *, const char *, const char *, void *, pid_t *);
82 static int selected(const char *);
83 static void addoption(char *);
84 static const char *getoptions(const char *);
85 static void addentry(struct fstypelist *, const char *, const char *);
86 static void maketypelist(char *);
87 static void catopt(char **, const char *);
88 static void mangle(char *, int *, const char ** volatile *, int *);
89 static const char *getfslab(const char *);
90 static void usage(void);
91 static void *isok(struct fstab *);
92
93 int
94 main(int argc, char *argv[])
95 {
96 struct fstab *fs;
97 int i, rval = 0;
98 const char *vfstype = NULL;
99 char globopt[3];
100
101 globopt[0] = '-';
102 globopt[2] = '\0';
103
104 TAILQ_INIT(&selhead);
105 TAILQ_INIT(&opthead);
106
107 while ((i = getopt(argc, argv, "dfl:nPpqT:t:vy")) != -1) {
108 switch (i) {
109 case 'd':
110 flags |= CHECK_DEBUG;
111 continue;
112
113 case 'f':
114 flags |= CHECK_FORCE;
115 break;
116
117 case 'n':
118 flags |= CHECK_NOFIX;
119 break;
120
121 case 'p':
122 flags |= CHECK_PREEN;
123 break;
124
125 case 'P':
126 flags |= CHECK_PROGRESS;
127 break;
128
129 case 'q':
130 break;
131
132 case 'l':
133 maxrun = atoi(optarg);
134 continue;
135
136 case 'T':
137 if (*optarg)
138 addoption(optarg);
139 continue;
140
141 case 't':
142 if (TAILQ_FIRST(&selhead) != NULL)
143 errx(1, "only one -t option may be specified.");
144
145 maketypelist(optarg);
146 vfstype = optarg;
147 continue;
148
149 case 'v':
150 flags |= CHECK_VERBOSE;
151 continue;
152
153 case 'y':
154 break;
155
156 case '?':
157 default:
158 usage();
159 /* NOTREACHED */
160 }
161
162 /* Pass option to fsck_xxxfs */
163 globopt[1] = i;
164 catopt(&options, globopt);
165 }
166
167 /* Don't do progress meters if we're debugging. */
168 if (flags & CHECK_DEBUG)
169 flags &= ~CHECK_PROGRESS;
170
171 /*
172 * If progress meters are being used, force max parallel to 1
173 * so the progress meter outputs don't interfere with one another.
174 */
175 if (flags & CHECK_PROGRESS)
176 maxrun = 1;
177
178 argc -= optind;
179 argv += optind;
180
181 if (argc == 0)
182 return checkfstab(flags, maxrun, isok, checkfs);
183
184 #define BADTYPE(type) \
185 (strcmp(type, FSTAB_RO) && \
186 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
187
188
189 for (; argc--; argv++) {
190 const char *spec, *type, *cp;
191 char device[MAXPATHLEN];
192
193 spec = *argv;
194 cp = strrchr(spec, '/');
195 if (cp == 0) {
196 (void)snprintf(device, sizeof(device), "%s%s",
197 _PATH_DEV, spec);
198 spec = device;
199 }
200 if ((fs = getfsfile(spec)) == NULL &&
201 (fs = getfsspec(spec)) == NULL) {
202 if (vfstype == NULL)
203 vfstype = getfslab(spec);
204 type = vfstype;
205 }
206 else {
207 spec = fs->fs_spec;
208 type = fs->fs_vfstype;
209 if (BADTYPE(fs->fs_type))
210 errx(1, "%s has unknown file system type.",
211 spec);
212 }
213
214 rval |= checkfs(type, blockcheck(spec), *argv, NULL, NULL);
215 }
216
217 return rval;
218 }
219
220
221 static void *
222 isok(struct fstab *fs)
223 {
224
225 if (fs->fs_passno == 0)
226 return NULL;
227
228 if (BADTYPE(fs->fs_type))
229 return NULL;
230
231 if (!selected(fs->fs_vfstype))
232 return NULL;
233
234 return fs;
235 }
236
237
238 static int
239 checkfs(const char *vfstype, const char *spec, const char *mntpt, void *auxarg,
240 pid_t *pidp)
241 {
242 /* List of directories containing fsck_xxx subcommands. */
243 static const char *edirs[] = {
244 #ifdef RESCUEDIR
245 RESCUEDIR,
246 #endif
247 _PATH_SBIN,
248 _PATH_USRSBIN,
249 NULL
250 };
251 const char ** volatile argv, **edir;
252 pid_t pid;
253 int argc, i, status, maxargc;
254 char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
255 const char *extra = getoptions(vfstype);
256
257 #ifdef __GNUC__
258 /* Avoid vfork clobbering */
259 (void) &optbuf;
260 (void) &vfstype;
261 #endif
262
263 if (!strcmp(vfstype, "ufs"))
264 vfstype = MOUNT_UFS;
265
266 optbuf = NULL;
267 if (options)
268 catopt(&optbuf, options);
269 if (extra)
270 catopt(&optbuf, extra);
271
272 maxargc = 64;
273 argv = emalloc(sizeof(char *) * maxargc);
274
275 (void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
276 argc = 0;
277 argv[argc++] = execbase;
278 if (optbuf)
279 mangle(optbuf, &argc, &argv, &maxargc);
280 argv[argc++] = spec;
281 argv[argc] = NULL;
282
283 if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
284 (void)printf("start %s %swait", mntpt,
285 pidp ? "no" : "");
286 for (i = 0; i < argc; i++)
287 (void)printf(" %s", argv[i]);
288 (void)printf("\n");
289 }
290
291 switch (pid = vfork()) {
292 case -1: /* Error. */
293 warn("vfork");
294 if (optbuf)
295 free(optbuf);
296 free(argv);
297 return (1);
298
299 case 0: /* Child. */
300 if ((flags & CHECK_FORCE) == 0) {
301 struct statvfs sfs;
302
303 /*
304 * if mntpt is a mountpoint of a mounted file
305 * system and it's mounted read-write, skip it
306 * unless -f is given.
307 */
308 if ((statvfs(mntpt, &sfs) == 0) &&
309 (strcmp(mntpt, sfs.f_mntonname) == 0) &&
310 ((sfs.f_flag & MNT_RDONLY) == 0)) {
311 printf(
312 "%s: file system is mounted read-write on %s; not checking\n",
313 spec, mntpt);
314 if ((flags & CHECK_PREEN) && auxarg != NULL)
315 _exit(0); /* fsck -p */
316 else
317 _exit(1); /* fsck [[-p] ...] */
318 }
319 }
320
321 if (flags & CHECK_DEBUG)
322 _exit(0);
323
324 /* Go find an executable. */
325 edir = edirs;
326 do {
327 (void)snprintf(execname,
328 sizeof(execname), "%s/%s", *edir, execbase);
329 execv(execname, (char * const *)__UNCONST(argv));
330 if (errno != ENOENT) {
331 if (spec)
332 warn("exec %s for %s", execname, spec);
333 else
334 warn("exec %s", execname);
335 }
336 } while (*++edir != NULL);
337
338 if (errno == ENOENT) {
339 if (spec)
340 warn("exec %s for %s", execname, spec);
341 else
342 warn("exec %s", execname);
343 }
344 _exit(1);
345 /* NOTREACHED */
346
347 default: /* Parent. */
348 if (optbuf)
349 free(optbuf);
350 free(argv);
351
352 if (pidp) {
353 *pidp = pid;
354 return 0;
355 }
356
357 if (waitpid(pid, &status, 0) < 0) {
358 warn("waitpid");
359 return (1);
360 }
361
362 if (WIFEXITED(status)) {
363 if (WEXITSTATUS(status) != 0)
364 return (WEXITSTATUS(status));
365 }
366 else if (WIFSIGNALED(status)) {
367 warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
368 return (1);
369 }
370 break;
371 }
372
373 return (0);
374 }
375
376
377 static int
378 selected(const char *type)
379 {
380 struct entry *e;
381
382 /* If no type specified, it's always selected. */
383 TAILQ_FOREACH(e, &selhead, entries)
384 if (!strncmp(e->type, type, MFSNAMELEN))
385 return which == IN_LIST ? 1 : 0;
386
387 return which == IN_LIST ? 0 : 1;
388 }
389
390
391 static const char *
392 getoptions(const char *type)
393 {
394 struct entry *e;
395
396 TAILQ_FOREACH(e, &opthead, entries)
397 if (!strncmp(e->type, type, MFSNAMELEN))
398 return e->options;
399 return "";
400 }
401
402
403 static void
404 addoption(char *optstr)
405 {
406 char *newoptions;
407 struct entry *e;
408
409 if ((newoptions = strchr(optstr, ':')) == NULL)
410 errx(1, "Invalid option string");
411
412 *newoptions++ = '\0';
413
414 TAILQ_FOREACH(e, &opthead, entries)
415 if (!strncmp(e->type, optstr, MFSNAMELEN)) {
416 catopt(&e->options, newoptions);
417 return;
418 }
419 addentry(&opthead, optstr, newoptions);
420 }
421
422
423 static void
424 addentry(struct fstypelist *list, const char *type, const char *opts)
425 {
426 struct entry *e;
427
428 e = emalloc(sizeof(struct entry));
429 e->type = estrdup(type);
430 e->options = estrdup(opts);
431 TAILQ_INSERT_TAIL(list, e, entries);
432 }
433
434
435 static void
436 maketypelist(char *fslist)
437 {
438 char *ptr;
439
440 if ((fslist == NULL) || (fslist[0] == '\0'))
441 errx(1, "empty type list");
442
443 if (fslist[0] == 'n' && fslist[1] == 'o') {
444 fslist += 2;
445 which = NOT_IN_LIST;
446 }
447 else
448 which = IN_LIST;
449
450 while ((ptr = strsep(&fslist, ",")) != NULL)
451 addentry(&selhead, ptr, "");
452
453 }
454
455
456 static void
457 catopt(char **sp, const char *o)
458 {
459 char *s;
460 size_t i, j;
461
462 s = *sp;
463 if (s) {
464 i = strlen(s);
465 j = i + 1 + strlen(o) + 1;
466 s = erealloc(s, j);
467 (void)snprintf(s + i, j, ",%s", o);
468 } else
469 s = estrdup(o);
470 *sp = s;
471 }
472
473
474 static void
475 mangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
476 {
477 char *p, *s;
478 int argc, maxargc;
479 const char **argv;
480
481 argc = *argcp;
482 argv = *argvp;
483 maxargc = *maxargcp;
484
485 for (s = opts; (p = strsep(&s, ",")) != NULL;) {
486 /* Always leave space for one more argument and the NULL. */
487 if (argc >= maxargc - 3) {
488 maxargc <<= 1;
489 argv = erealloc(argv, maxargc * sizeof(char *));
490 }
491 if (*p != '\0') {
492 if (*p == '-') {
493 argv[argc++] = p;
494 p = strchr(p, '=');
495 if (p) {
496 *p = '\0';
497 argv[argc++] = p+1;
498 }
499 } else {
500 argv[argc++] = "-o";
501 argv[argc++] = p;
502 }
503 }
504 }
505
506 *argcp = argc;
507 *argvp = argv;
508 *maxargcp = maxargc;
509 }
510
511 static const char *
512 getfslab(const char *str)
513 {
514 static struct dkwedge_info dkw;
515 struct disklabel dl;
516 int fd;
517 char p;
518 const char *vfstype;
519 u_char t;
520
521 /* deduce the file system type from the disk label */
522 if ((fd = open(str, O_RDONLY)) == -1)
523 err(1, "cannot open `%s'", str);
524
525 /* First check to see if it's a wedge. */
526 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
527 /* Yup, this is easy. */
528 (void) close(fd);
529 return (dkw.dkw_ptype);
530 }
531
532 if (ioctl(fd, DIOCGDINFO, &dl) == -1)
533 err(1, "cannot get disklabel for `%s'", str);
534
535 (void) close(fd);
536
537 p = str[strlen(str) - 1];
538
539 if ((p - 'a') >= dl.d_npartitions)
540 errx(1, "partition `%s' is not defined on disk", str);
541
542 if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES)
543 errx(1, "partition `%s' is not of a legal vfstype",
544 str);
545
546 if ((vfstype = fscknames[t]) == NULL)
547 errx(1, "vfstype `%s' on partition `%s' is not supported",
548 fstypenames[t], str);
549
550 return vfstype;
551 }
552
553
554 static void
555 usage(void)
556 {
557 static const char common[] =
558 "[-dfnPpqvy] [-l maxparallel] [-T fstype:fsoptions]\n\t\t[-t fstype]";
559
560 (void)fprintf(stderr, "usage: %s %s [special|node]...\n",
561 getprogname(), common);
562 exit(1);
563 }
564