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