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