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