fsck.c revision 1.19 1 /* $NetBSD: fsck.c,v 1.19 1998/08/25 19:18:14 ross 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.19 1998/08/25 19:18:14 ross 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 void catopt __P((char **, const char *));
91 static void mangle __P((char *, int *, const char ***, int *));
92 static const 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 const 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 catopt(&options, globopt);
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 const 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 const char **argv, **edir;
221 pid_t pid;
222 int argc, i, status, maxargc;
223 char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
224 const char *extra = getoptions(vfstype);
225
226 #ifdef __GNUC__
227 /* Avoid vfork clobbering */
228 (void) &optbuf;
229 (void) &vfstype;
230 #endif
231
232 if (!strcmp(vfstype, "ufs"))
233 vfstype = MOUNT_UFS;
234
235 optbuf = NULL;
236 if (options)
237 catopt(&optbuf, options);
238 if (extra)
239 catopt(&optbuf, extra);
240
241 maxargc = 64;
242 argv = emalloc(sizeof(char *) * maxargc);
243
244 (void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
245 argc = 0;
246 argv[argc++] = execbase;
247 if (optbuf)
248 mangle(optbuf, &argc, &argv, &maxargc);
249 argv[argc++] = spec;
250 argv[argc] = NULL;
251
252 if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
253 (void)printf("start %s %swait", mntpt,
254 pidp ? "no" : "");
255 for (i = 0; i < argc; i++)
256 (void)printf(" %s", argv[i]);
257 (void)printf("\n");
258 }
259
260 switch (pid = vfork()) {
261 case -1: /* Error. */
262 warn("vfork");
263 if (optbuf)
264 free(optbuf);
265 return (1);
266
267 case 0: /* Child. */
268 if (flags & CHECK_DEBUG)
269 _exit(0);
270
271 /* Go find an executable. */
272 edir = edirs;
273 do {
274 (void)snprintf(execname,
275 sizeof(execname), "%s/%s", *edir, execbase);
276 execv(execname, (char * const *)argv);
277 if (errno != ENOENT) {
278 if (spec)
279 warn("exec %s for %s", execname, spec);
280 else
281 warn("exec %s", execname);
282 }
283 } while (*++edir != NULL);
284
285 if (errno == ENOENT) {
286 if (spec)
287 warn("exec %s for %s", execname, spec);
288 else
289 warn("exec %s", execname);
290 }
291 _exit(1);
292 /* NOTREACHED */
293
294 default: /* Parent. */
295 if (optbuf)
296 free(optbuf);
297
298 if (pidp) {
299 *pidp = pid;
300 return 0;
301 }
302
303 if (waitpid(pid, &status, 0) < 0) {
304 warn("waitpid");
305 return (1);
306 }
307
308 if (WIFEXITED(status)) {
309 if (WEXITSTATUS(status) != 0)
310 return (WEXITSTATUS(status));
311 }
312 else if (WIFSIGNALED(status)) {
313 warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
314 return (1);
315 }
316 break;
317 }
318
319 return (0);
320 }
321
322
323 static int
324 selected(type)
325 const char *type;
326 {
327 struct entry *e;
328
329 /* If no type specified, it's always selected. */
330 for (e = selhead.tqh_first; e != NULL; e = e->entries.tqe_next)
331 if (!strncmp(e->type, type, MFSNAMELEN))
332 return which == IN_LIST ? 1 : 0;
333
334 return which == IN_LIST ? 0 : 1;
335 }
336
337
338 static const char *
339 getoptions(type)
340 const char *type;
341 {
342 struct entry *e;
343
344 for (e = opthead.tqh_first; e != NULL; e = e->entries.tqe_next)
345 if (!strncmp(e->type, type, MFSNAMELEN))
346 return e->options;
347 return "";
348 }
349
350
351 static void
352 addoption(optstr)
353 char *optstr;
354 {
355 char *newoptions;
356 struct entry *e;
357
358 if ((newoptions = strchr(optstr, ':')) == NULL)
359 errx(1, "Invalid option string");
360
361 *newoptions++ = '\0';
362
363 for (e = opthead.tqh_first; e != NULL; e = e->entries.tqe_next)
364 if (!strncmp(e->type, optstr, MFSNAMELEN)) {
365 catopt(&e->options, newoptions);
366 return;
367 }
368 addentry(&opthead, optstr, newoptions);
369 }
370
371
372 static void
373 addentry(list, type, opts)
374 struct fstypelist *list;
375 const char *type;
376 const char *opts;
377 {
378 struct entry *e;
379
380 e = emalloc(sizeof(struct entry));
381 e->type = estrdup(type);
382 e->options = estrdup(opts);
383 TAILQ_INSERT_TAIL(list, e, entries);
384 }
385
386
387 static void
388 maketypelist(fslist)
389 char *fslist;
390 {
391 char *ptr;
392
393 if ((fslist == NULL) || (fslist[0] == '\0'))
394 errx(1, "empty type list");
395
396 if (fslist[0] == 'n' && fslist[1] == 'o') {
397 fslist += 2;
398 which = NOT_IN_LIST;
399 }
400 else
401 which = IN_LIST;
402
403 while ((ptr = strsep(&fslist, ",")) != NULL)
404 addentry(&selhead, ptr, "");
405
406 }
407
408
409 static void
410 catopt(sp, o)
411 char **sp;
412 const char *o;
413 {
414 char *s;
415 size_t i, j;
416
417 s = *sp;
418 if (s) {
419 i = strlen(s);
420 j = i + 1 + strlen(o) + 1;
421 s = erealloc(s, j);
422 (void)snprintf(s + i, j, ",%s", o);
423 } else
424 s = estrdup(o);
425 *sp = s;
426 }
427
428
429 static void
430 mangle(options, argcp, argvp, maxargcp)
431 char *options;
432 int *argcp, *maxargcp;
433 const char ***argvp;
434 {
435 char *p, *s;
436 int argc, maxargc;
437 const char **argv;
438
439 argc = *argcp;
440 argv = *argvp;
441 maxargc = *maxargcp;
442
443 for (s = options; (p = strsep(&s, ",")) != NULL;) {
444 /* Always leave space for one more argument and the NULL. */
445 if (argc >= maxargc - 3) {
446 maxargc <<= 1;
447 argv = erealloc(argv, maxargc * sizeof(char *));
448 }
449 if (*p != '\0') {
450 if (*p == '-') {
451 argv[argc++] = p;
452 p = strchr(p, '=');
453 if (p) {
454 *p = '\0';
455 argv[argc++] = p+1;
456 }
457 } else {
458 argv[argc++] = "-o";
459 argv[argc++] = p;
460 }
461 }
462 }
463
464 *argcp = argc;
465 *argvp = argv;
466 *maxargcp = maxargc;
467 }
468
469
470 const static char *
471 getfslab(str)
472 const char *str;
473 {
474 struct disklabel dl;
475 int fd;
476 char p;
477 const char *vfstype;
478 u_char t;
479
480 /* deduce the filesystem type from the disk label */
481 if ((fd = open(str, O_RDONLY)) == -1)
482 err(1, "cannot open `%s'", str);
483
484 if (ioctl(fd, DIOCGDINFO, &dl) == -1)
485 err(1, "cannot get disklabel for `%s'", str);
486
487 (void) close(fd);
488
489 p = str[strlen(str) - 1];
490
491 if ((p - 'a') >= dl.d_npartitions)
492 errx(1, "partition `%s' is not defined on disk", str);
493
494 if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES)
495 errx(1, "partition `%s' is not of a legal vfstype",
496 str);
497
498 if ((vfstype = fscknames[t]) == NULL)
499 errx(1, "vfstype `%s' on partition `%s' is not supported",
500 fstypenames[t], str);
501
502 return vfstype;
503 }
504
505
506 static void
507 usage()
508 {
509 extern char *__progname;
510 static const char common[] =
511 "[-dpvlyn] [-T fstype:fsoptions] [-t fstype]";
512
513 (void)fprintf(stderr, "Usage: %s %s [special|node]...\n",
514 __progname, common);
515 exit(1);
516 }
517