fsck.c revision 1.45 1 /* $NetBSD: fsck.c,v 1.45 2006/12/13 16:08:26 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.45 2006/12/13 16:08:26 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 *vfst, 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 const char * volatile vfstype = vfst;
253 pid_t pid;
254 int argc, i, status, maxargc;
255 char *optb;
256 char *volatile optbuf;
257 char execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
258 const char *extra = getoptions(vfstype);
259
260 if (!strcmp(vfstype, "ufs"))
261 vfstype = MOUNT_UFS;
262
263 optb = NULL;
264 if (options)
265 catopt(&optb, options);
266 if (extra)
267 catopt(&optb, extra);
268 optbuf = optb;
269
270 maxargc = 64;
271 argv = emalloc(sizeof(char *) * maxargc);
272
273 (void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
274 argc = 0;
275 argv[argc++] = execbase;
276 if (optbuf)
277 mangle(optbuf, &argc, &argv, &maxargc);
278 argv[argc++] = spec;
279 argv[argc] = NULL;
280
281 if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
282 (void)printf("start %s %swait", mntpt,
283 pidp ? "no" : "");
284 for (i = 0; i < argc; i++)
285 (void)printf(" %s", argv[i]);
286 (void)printf("\n");
287 }
288
289 switch (pid = vfork()) {
290 case -1: /* Error. */
291 warn("vfork");
292 if (optbuf)
293 free(optbuf);
294 free(argv);
295 return (1);
296
297 case 0: /* Child. */
298 if ((flags & CHECK_FORCE) == 0) {
299 struct statvfs sfs;
300
301 /*
302 * if mntpt is a mountpoint of a mounted file
303 * system and it's mounted read-write, skip it
304 * unless -f is given.
305 */
306 if ((statvfs(mntpt, &sfs) == 0) &&
307 (strcmp(mntpt, sfs.f_mntonname) == 0) &&
308 ((sfs.f_flag & MNT_RDONLY) == 0)) {
309 printf(
310 "%s: file system is mounted read-write on %s; not checking\n",
311 spec, mntpt);
312 if ((flags & CHECK_PREEN) && auxarg != NULL)
313 _exit(0); /* fsck -p */
314 else
315 _exit(1); /* fsck [[-p] ...] */
316 }
317 }
318
319 if (flags & CHECK_DEBUG)
320 _exit(0);
321
322 /* Go find an executable. */
323 edir = edirs;
324 do {
325 (void)snprintf(execname,
326 sizeof(execname), "%s/%s", *edir, execbase);
327 execv(execname, (char * const *)__UNCONST(argv));
328 if (errno != ENOENT) {
329 if (spec)
330 warn("exec %s for %s", execname, spec);
331 else
332 warn("exec %s", execname);
333 }
334 } while (*++edir != NULL);
335
336 if (errno == ENOENT) {
337 if (spec)
338 warn("exec %s for %s", execname, spec);
339 else
340 warn("exec %s", execname);
341 }
342 _exit(1);
343 /* NOTREACHED */
344
345 default: /* Parent. */
346 if (optbuf)
347 free(optbuf);
348 free(argv);
349
350 if (pidp) {
351 *pidp = pid;
352 return 0;
353 }
354
355 if (waitpid(pid, &status, 0) < 0) {
356 warn("waitpid");
357 return (1);
358 }
359
360 if (WIFEXITED(status)) {
361 if (WEXITSTATUS(status) != 0)
362 return (WEXITSTATUS(status));
363 }
364 else if (WIFSIGNALED(status)) {
365 warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
366 return (1);
367 }
368 break;
369 }
370
371 return (0);
372 }
373
374
375 static int
376 selected(const char *type)
377 {
378 struct entry *e;
379
380 /* If no type specified, it's always selected. */
381 TAILQ_FOREACH(e, &selhead, entries)
382 if (!strncmp(e->type, type, MFSNAMELEN))
383 return which == IN_LIST ? 1 : 0;
384
385 return which == IN_LIST ? 0 : 1;
386 }
387
388
389 static const char *
390 getoptions(const char *type)
391 {
392 struct entry *e;
393
394 TAILQ_FOREACH(e, &opthead, entries)
395 if (!strncmp(e->type, type, MFSNAMELEN))
396 return e->options;
397 return "";
398 }
399
400
401 static void
402 addoption(char *optstr)
403 {
404 char *newoptions;
405 struct entry *e;
406
407 if ((newoptions = strchr(optstr, ':')) == NULL)
408 errx(1, "Invalid option string");
409
410 *newoptions++ = '\0';
411
412 TAILQ_FOREACH(e, &opthead, entries)
413 if (!strncmp(e->type, optstr, MFSNAMELEN)) {
414 catopt(&e->options, newoptions);
415 return;
416 }
417 addentry(&opthead, optstr, newoptions);
418 }
419
420
421 static void
422 addentry(struct fstypelist *list, const char *type, const char *opts)
423 {
424 struct entry *e;
425
426 e = emalloc(sizeof(struct entry));
427 e->type = estrdup(type);
428 e->options = estrdup(opts);
429 TAILQ_INSERT_TAIL(list, e, entries);
430 }
431
432
433 static void
434 maketypelist(char *fslist)
435 {
436 char *ptr;
437
438 if ((fslist == NULL) || (fslist[0] == '\0'))
439 errx(1, "empty type list");
440
441 if (fslist[0] == 'n' && fslist[1] == 'o') {
442 fslist += 2;
443 which = NOT_IN_LIST;
444 }
445 else
446 which = IN_LIST;
447
448 while ((ptr = strsep(&fslist, ",")) != NULL)
449 addentry(&selhead, ptr, "");
450
451 }
452
453
454 static void
455 catopt(char **sp, const char *o)
456 {
457 char *s;
458 size_t i, j;
459
460 s = *sp;
461 if (s) {
462 i = strlen(s);
463 j = i + 1 + strlen(o) + 1;
464 s = erealloc(s, j);
465 (void)snprintf(s + i, j, ",%s", o);
466 } else
467 s = estrdup(o);
468 *sp = s;
469 }
470
471
472 static void
473 mangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
474 {
475 char *p, *s;
476 int argc, maxargc;
477 const char **argv;
478
479 argc = *argcp;
480 argv = *argvp;
481 maxargc = *maxargcp;
482
483 for (s = opts; (p = strsep(&s, ",")) != NULL;) {
484 /* Always leave space for one more argument and the NULL. */
485 if (argc >= maxargc - 3) {
486 maxargc <<= 1;
487 argv = erealloc(argv, maxargc * sizeof(char *));
488 }
489 if (*p != '\0') {
490 if (*p == '-') {
491 argv[argc++] = p;
492 p = strchr(p, '=');
493 if (p) {
494 *p = '\0';
495 argv[argc++] = p+1;
496 }
497 } else {
498 argv[argc++] = "-o";
499 argv[argc++] = p;
500 }
501 }
502 }
503
504 *argcp = argc;
505 *argvp = argv;
506 *maxargcp = maxargc;
507 }
508
509 static const char *
510 getfslab(const char *str)
511 {
512 static struct dkwedge_info dkw;
513 struct disklabel dl;
514 int fd;
515 char p;
516 const char *vfstype;
517 u_char t;
518
519 /* deduce the file system type from the disk label */
520 if ((fd = open(str, O_RDONLY)) == -1)
521 err(1, "cannot open `%s'", str);
522
523 /* First check to see if it's a wedge. */
524 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
525 /* Yup, this is easy. */
526 (void) close(fd);
527 return (dkw.dkw_ptype);
528 }
529
530 if (ioctl(fd, DIOCGDINFO, &dl) == -1)
531 err(1, "cannot get disklabel for `%s'", str);
532
533 (void) close(fd);
534
535 p = str[strlen(str) - 1];
536
537 if ((p - 'a') >= dl.d_npartitions)
538 errx(1, "partition `%s' is not defined on disk", str);
539
540 if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES)
541 errx(1, "partition `%s' is not of a legal vfstype",
542 str);
543
544 if ((vfstype = fscknames[t]) == NULL)
545 errx(1, "vfstype `%s' on partition `%s' is not supported",
546 fstypenames[t], str);
547
548 return vfstype;
549 }
550
551
552 static void
553 usage(void)
554 {
555 static const char common[] =
556 "[-dfnPpqvy] [-l maxparallel] [-T fstype:fsoptions]\n\t\t[-t fstype]";
557
558 (void)fprintf(stderr, "usage: %s %s [special|node]...\n",
559 getprogname(), common);
560 exit(1);
561 }
562