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