fsck.c revision 1.33 1 /* $NetBSD: fsck.c,v 1.33 2004/03/20 20:28:44 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.33 2004/03/20 20:28:44 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/disklabel.h>
49 #include <sys/ioctl.h>
50
51 #include <err.h>
52 #include <errno.h>
53 #include <fstab.h>
54 #include <fcntl.h>
55 #include <paths.h>
56 #include <signal.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 #include "pathnames.h"
63 #include "fsutil.h"
64
65 static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
66
67 TAILQ_HEAD(fstypelist, entry) opthead, selhead;
68
69 struct entry {
70 char *type;
71 char *options;
72 TAILQ_ENTRY(entry) entries;
73 };
74
75 static int maxrun = 0;
76 static char *options = NULL;
77 static int flags = 0;
78
79 int main(int, char *[]);
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 ***, 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:npqT: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 break;
119
120 case 'p':
121 flags |= CHECK_PREEN;
122 break;
123
124 case 'q':
125 break;
126
127 case 'l':
128 maxrun = atoi(optarg);
129 continue;
130
131 case 'T':
132 if (*optarg)
133 addoption(optarg);
134 continue;
135
136 case 't':
137 if (TAILQ_FIRST(&selhead) != NULL)
138 errx(1, "only one -t option may be specified.");
139
140 maketypelist(optarg);
141 vfstype = optarg;
142 continue;
143
144 case 'v':
145 flags |= CHECK_VERBOSE;
146 continue;
147
148 case 'y':
149 break;
150
151 case '?':
152 default:
153 usage();
154 /* NOTREACHED */
155 }
156
157 /* Pass option to fsck_xxxfs */
158 globopt[1] = i;
159 catopt(&options, globopt);
160 }
161
162 argc -= optind;
163 argv += optind;
164
165 if (argc == 0)
166 return checkfstab(flags, maxrun, isok, checkfs);
167
168 #define BADTYPE(type) \
169 (strcmp(type, FSTAB_RO) && \
170 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
171
172
173 for (; argc--; argv++) {
174 const char *spec, *type, *cp;
175 char device[MAXPATHLEN];
176
177 spec = *argv;
178 cp = strrchr(spec, '/');
179 if (cp == 0) {
180 (void)snprintf(device, sizeof(device), "%s%s",
181 _PATH_DEV, spec);
182 spec = device;
183 }
184 if ((fs = getfsfile(spec)) == NULL &&
185 (fs = getfsspec(spec)) == NULL) {
186 if (vfstype == NULL)
187 vfstype = getfslab(spec);
188 type = vfstype;
189 }
190 else {
191 spec = fs->fs_spec;
192 type = fs->fs_vfstype;
193 if (BADTYPE(fs->fs_type))
194 errx(1, "%s has unknown file system type.",
195 spec);
196 }
197
198 rval |= checkfs(type, blockcheck(spec), *argv, NULL, NULL);
199 }
200
201 return rval;
202 }
203
204
205 static void *
206 isok(struct fstab *fs)
207 {
208
209 if (fs->fs_passno == 0)
210 return NULL;
211
212 if (BADTYPE(fs->fs_type))
213 return NULL;
214
215 if (!selected(fs->fs_vfstype))
216 return NULL;
217
218 return fs;
219 }
220
221
222 static int
223 checkfs(const char *vfstype, const char *spec, const char *mntpt, void *auxarg,
224 pid_t *pidp)
225 {
226 /* List of directories containing fsck_xxx subcommands. */
227 static const char *edirs[] = {
228 #ifdef _PATH_RESCUE
229 _PATH_RESCUE,
230 #endif
231 _PATH_SBIN,
232 _PATH_USRSBIN,
233 NULL
234 };
235 const char **argv, **edir;
236 pid_t pid;
237 int argc, i, status, maxargc;
238 char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
239 const char *extra = getoptions(vfstype);
240
241 #ifdef __GNUC__
242 /* Avoid vfork clobbering */
243 (void) &optbuf;
244 (void) &vfstype;
245 #endif
246
247 if (!strcmp(vfstype, "ufs"))
248 vfstype = MOUNT_UFS;
249
250 optbuf = NULL;
251 if (options)
252 catopt(&optbuf, options);
253 if (extra)
254 catopt(&optbuf, extra);
255
256 maxargc = 64;
257 argv = emalloc(sizeof(char *) * maxargc);
258
259 (void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
260 argc = 0;
261 argv[argc++] = execbase;
262 if (optbuf)
263 mangle(optbuf, &argc, &argv, &maxargc);
264 argv[argc++] = spec;
265 argv[argc] = NULL;
266
267 if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
268 (void)printf("start %s %swait", mntpt,
269 pidp ? "no" : "");
270 for (i = 0; i < argc; i++)
271 (void)printf(" %s", argv[i]);
272 (void)printf("\n");
273 }
274
275 switch (pid = vfork()) {
276 case -1: /* Error. */
277 warn("vfork");
278 if (optbuf)
279 free(optbuf);
280 return (1);
281
282 case 0: /* Child. */
283 if ((flags & CHECK_FORCE) == 0) {
284 struct statfs sfs;
285
286 /*
287 * if mntpt is a mountpoint of a mounted file
288 * system and it's mounted read-write, skip it
289 * unless -f is given.
290 */
291 if ((statfs(mntpt, &sfs) == 0) &&
292 (strcmp(mntpt, sfs.f_mntonname) == 0) &&
293 ((sfs.f_flags & MNT_RDONLY) == 0)) {
294 printf(
295 "%s: file system is mounted read-write on %s; not checking\n",
296 spec, mntpt);
297 if ((flags & CHECK_PREEN) && auxarg != NULL)
298 _exit(0); /* fsck -p */
299 else
300 _exit(1); /* fsck [[-p] ...] */
301 }
302 }
303
304 if (flags & CHECK_DEBUG)
305 _exit(0);
306
307 /* Go find an executable. */
308 edir = edirs;
309 do {
310 (void)snprintf(execname,
311 sizeof(execname), "%s/%s", *edir, execbase);
312 execv(execname, (char * const *)argv);
313 if (errno != ENOENT) {
314 if (spec)
315 warn("exec %s for %s", execname, spec);
316 else
317 warn("exec %s", execname);
318 }
319 } while (*++edir != NULL);
320
321 if (errno == ENOENT) {
322 if (spec)
323 warn("exec %s for %s", execname, spec);
324 else
325 warn("exec %s", execname);
326 }
327 _exit(1);
328 /* NOTREACHED */
329
330 default: /* Parent. */
331 if (optbuf)
332 free(optbuf);
333
334 if (pidp) {
335 *pidp = pid;
336 return 0;
337 }
338
339 if (waitpid(pid, &status, 0) < 0) {
340 warn("waitpid");
341 return (1);
342 }
343
344 if (WIFEXITED(status)) {
345 if (WEXITSTATUS(status) != 0)
346 return (WEXITSTATUS(status));
347 }
348 else if (WIFSIGNALED(status)) {
349 warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
350 return (1);
351 }
352 break;
353 }
354
355 return (0);
356 }
357
358
359 static int
360 selected(const char *type)
361 {
362 struct entry *e;
363
364 /* If no type specified, it's always selected. */
365 TAILQ_FOREACH(e, &selhead, entries)
366 if (!strncmp(e->type, type, MFSNAMELEN))
367 return which == IN_LIST ? 1 : 0;
368
369 return which == IN_LIST ? 0 : 1;
370 }
371
372
373 static const char *
374 getoptions(const char *type)
375 {
376 struct entry *e;
377
378 TAILQ_FOREACH(e, &opthead, entries)
379 if (!strncmp(e->type, type, MFSNAMELEN))
380 return e->options;
381 return "";
382 }
383
384
385 static void
386 addoption(char *optstr)
387 {
388 char *newoptions;
389 struct entry *e;
390
391 if ((newoptions = strchr(optstr, ':')) == NULL)
392 errx(1, "Invalid option string");
393
394 *newoptions++ = '\0';
395
396 TAILQ_FOREACH(e, &opthead, entries)
397 if (!strncmp(e->type, optstr, MFSNAMELEN)) {
398 catopt(&e->options, newoptions);
399 return;
400 }
401 addentry(&opthead, optstr, newoptions);
402 }
403
404
405 static void
406 addentry(struct fstypelist *list, const char *type, const char *opts)
407 {
408 struct entry *e;
409
410 e = emalloc(sizeof(struct entry));
411 e->type = estrdup(type);
412 e->options = estrdup(opts);
413 TAILQ_INSERT_TAIL(list, e, entries);
414 }
415
416
417 static void
418 maketypelist(char *fslist)
419 {
420 char *ptr;
421
422 if ((fslist == NULL) || (fslist[0] == '\0'))
423 errx(1, "empty type list");
424
425 if (fslist[0] == 'n' && fslist[1] == 'o') {
426 fslist += 2;
427 which = NOT_IN_LIST;
428 }
429 else
430 which = IN_LIST;
431
432 while ((ptr = strsep(&fslist, ",")) != NULL)
433 addentry(&selhead, ptr, "");
434
435 }
436
437
438 static void
439 catopt(char **sp, const char *o)
440 {
441 char *s;
442 size_t i, j;
443
444 s = *sp;
445 if (s) {
446 i = strlen(s);
447 j = i + 1 + strlen(o) + 1;
448 s = erealloc(s, j);
449 (void)snprintf(s + i, j, ",%s", o);
450 } else
451 s = estrdup(o);
452 *sp = s;
453 }
454
455
456 static void
457 mangle(char *opts, int *argcp, const char ***argvp, int *maxargcp)
458 {
459 char *p, *s;
460 int argc, maxargc;
461 const char **argv;
462
463 argc = *argcp;
464 argv = *argvp;
465 maxargc = *maxargcp;
466
467 for (s = opts; (p = strsep(&s, ",")) != NULL;) {
468 /* Always leave space for one more argument and the NULL. */
469 if (argc >= maxargc - 3) {
470 maxargc <<= 1;
471 argv = erealloc(argv, maxargc * sizeof(char *));
472 }
473 if (*p != '\0') {
474 if (*p == '-') {
475 argv[argc++] = p;
476 p = strchr(p, '=');
477 if (p) {
478 *p = '\0';
479 argv[argc++] = p+1;
480 }
481 } else {
482 argv[argc++] = "-o";
483 argv[argc++] = p;
484 }
485 }
486 }
487
488 *argcp = argc;
489 *argvp = argv;
490 *maxargcp = maxargc;
491 }
492
493
494 const static char *
495 getfslab(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 file system 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 = fscknames[t]) == NULL)
522 errx(1, "vfstype `%s' on partition `%s' is not supported",
523 fstypenames[t], str);
524
525 return vfstype;
526 }
527
528
529 static void
530 usage(void)
531 {
532 static const char common[] =
533 "[-dfnpqvy] [-T fstype:fsoptions] [-t fstype]";
534
535 (void)fprintf(stderr, "usage: %s %s [special|node]...\n",
536 getprogname(), common);
537 exit(1);
538 }
539