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