1 /* $OpenBSD: fsck.c,v 1.35 2015/04/18 18:28:37 deraadt Exp $ */ 2 /* $NetBSD: fsck.c,v 1.7 1996/10/03 20:06:30 christos Exp $ */ 3 4 /* 5 * Copyright (c) 1996 Christos Zoulas. All rights reserved. 6 * Copyright (c) 1980, 1989, 1993, 1994 7 * The Regents of the University of California. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * From: @(#)mount.c 8.19 (Berkeley) 4/19/94 34 * From: NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp 35 * 36 */ 37 38 #include <sys/types.h> 39 #include <sys/mount.h> 40 #include <sys/queue.h> 41 #include <sys/resource.h> 42 #include <sys/wait.h> 43 44 #include <err.h> 45 #include <errno.h> 46 #include <fstab.h> 47 #include <signal.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 #include <limits.h> 53 #include <util.h> 54 55 #include "pathnames.h" 56 #include "fsutil.h" 57 58 static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST; 59 static enum { NONET_FILTER, NET_FILTER } filter = NONET_FILTER; 60 61 TAILQ_HEAD(fstypelist, entry) opthead, selhead; 62 63 struct entry { 64 char *type; 65 char *options; 66 TAILQ_ENTRY(entry) entries; 67 }; 68 69 static int maxrun; 70 static char *options; 71 static int flags; 72 73 int main(int, char *[]); 74 75 static int checkfs(const char *, const char *, const char *, void *, pid_t *); 76 static int selected(const char *); 77 static void addoption(char *); 78 static const char *getoptions(const char *); 79 static void addentry(struct fstypelist *, const char *, const char *); 80 static void maketypelist(char *); 81 static char *catopt(char *, const char *, int); 82 static void mangle(char *, int *, const char ***, int *); 83 static void usage(void); 84 static void *isok(struct fstab *); 85 static int hasopt(const char *, const char *); 86 87 88 int 89 main(int argc, char *argv[]) 90 { 91 const char *errstr; 92 struct fstab *fs; 93 int i, rval = 0; 94 char *vfstype = NULL; 95 char *p, globopt[3]; 96 struct rlimit rl; 97 98 /* Increase our data size to the max */ 99 if (getrlimit(RLIMIT_DATA, &rl) == 0) { 100 if (geteuid() == 0) 101 rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; 102 else 103 rl.rlim_cur = rl.rlim_max; 104 if (setrlimit(RLIMIT_DATA, &rl) < 0) 105 warn("Can't set resource limit to max data size"); 106 } else 107 warn("Can't get resource limit for data size"); 108 109 globopt[0] = '-'; 110 globopt[2] = '\0'; 111 112 TAILQ_INIT(&selhead); 113 TAILQ_INIT(&opthead); 114 115 while ((i = getopt(argc, argv, "b:dfl:nNpT:t:vy")) != -1) 116 switch (i) { 117 case 'd': 118 flags |= CHECK_DEBUG; 119 break; 120 121 case 'v': 122 flags |= CHECK_VERBOSE; 123 break; 124 125 case 'p': 126 flags |= CHECK_PREEN; 127 /*FALLTHROUGH*/ 128 case 'n': 129 case 'f': 130 case 'y': 131 globopt[1] = i; 132 options = catopt(options, globopt, 1); 133 break; 134 135 case 'b': 136 if (asprintf(&p, "-b %s", optarg) == -1) 137 err(1, "malloc failed"); 138 options = catopt(options, p, 1); 139 free(p); 140 break; 141 142 case 'l': 143 maxrun = strtonum(optarg, 0, INT_MAX, &errstr); 144 if (errstr) 145 errx(1, "-l %s: %s", optarg, errstr); 146 147 break; 148 149 case 'T': 150 if (*optarg) 151 addoption(optarg); 152 break; 153 154 case 't': 155 if (!TAILQ_EMPTY(&selhead)) 156 errx(1, "only one -t option may be specified."); 157 158 maketypelist(optarg); 159 vfstype = optarg; 160 break; 161 162 case 'N': 163 filter = NET_FILTER; 164 break; 165 166 case '?': 167 default: 168 usage(); 169 /* NOTREACHED */ 170 } 171 172 argc -= optind; 173 argv += optind; 174 175 if (argc == 0) 176 return checkfstab(flags, maxrun, isok, checkfs); 177 178 #define BADTYPE(type) \ 179 (strcmp(type, FSTAB_RO) && \ 180 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) 181 182 183 for (; argc--; argv++) { 184 char *spec, *type; 185 186 if ((strncmp(*argv, "/dev/", 5) == 0 || isduid(*argv, 0)) && 187 (type = readlabelfs(*argv, 0))) { 188 spec = *argv; 189 } else if ((fs = getfsfile(*argv)) == NULL && 190 (fs = getfsspec(*argv)) == NULL) { 191 if (vfstype == NULL) 192 errx(1, 193 "%s: unknown special file or file system.", 194 *argv); 195 spec = *argv; 196 type = vfstype; 197 } else { 198 spec = fs->fs_spec; 199 type = fs->fs_vfstype; 200 if (BADTYPE(fs->fs_type)) 201 errx(1, "%s has unknown file system type.", 202 *argv); 203 } 204 205 rval |= checkfs(type, blockcheck(spec), *argv, NULL, NULL); 206 } 207 208 return rval; 209 } 210 211 212 static void * 213 isok(struct fstab *fs) 214 { 215 if (fs->fs_passno == 0) 216 return NULL; 217 218 if (BADTYPE(fs->fs_type)) 219 return NULL; 220 221 switch (filter) { 222 case NET_FILTER: 223 if (!hasopt(fs->fs_mntops, "net")) 224 return NULL; 225 break; 226 case NONET_FILTER: 227 if (hasopt(fs->fs_mntops, "net")) 228 return NULL; 229 break; 230 } 231 if (!selected(fs->fs_vfstype)) 232 return NULL; 233 234 return fs; 235 } 236 237 238 static int 239 checkfs(const char *vfstype, const char *spec, const char *mntpt, void *auxarg, 240 pid_t *pidp) 241 { 242 /* List of directories containing fsck_xxx subcommands. */ 243 static const char *edirs[] = { 244 _PATH_SBIN, 245 _PATH_USRSBIN, 246 NULL 247 }; 248 const char **argv, **edir; 249 pid_t pid; 250 int argc, i, status, maxargc; 251 char *optbuf = NULL, fsname[PATH_MAX], execname[PATH_MAX]; 252 const char *extra = getoptions(vfstype); 253 254 if (strcmp(vfstype, "ufs") == 0) 255 vfstype = MOUNT_UFS; 256 257 maxargc = 100; 258 argv = ereallocarray(NULL, maxargc, sizeof(char *)); 259 260 argc = 0; 261 (void)snprintf(fsname, sizeof(fsname), "fsck_%s", vfstype); 262 argv[argc++] = fsname; 263 264 if (options) { 265 if (extra != NULL) 266 optbuf = catopt(options, extra, 0); 267 else 268 optbuf = estrdup(options); 269 } 270 else if (extra) 271 optbuf = estrdup(extra); 272 273 if (optbuf) 274 mangle(optbuf, &argc, &argv, &maxargc); 275 276 argv[argc++] = spec; 277 argv[argc] = NULL; 278 279 if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) { 280 (void)printf("start %s %swait %s", mntpt, 281 pidp ? "no" : "", fsname); 282 for (i = 1; i < argc; i++) 283 (void)printf(" %s", argv[i]); 284 (void)printf("\n"); 285 } 286 287 switch (pid = fork()) { 288 case -1: /* Error. */ 289 warn("fork"); 290 if (optbuf) 291 free(optbuf); 292 free(argv); 293 return (1); 294 295 case 0: /* Child. */ 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/fsck_%s", *edir, vfstype); 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 free(argv); 326 327 if (pidp) { 328 *pidp = pid; 329 return 0; 330 } 331 332 if (waitpid(pid, &status, 0) < 0) { 333 warn("waitpid"); 334 return (1); 335 } 336 337 if (WIFEXITED(status)) { 338 if (WEXITSTATUS(status) != 0) 339 return (WEXITSTATUS(status)); 340 } 341 else if (WIFSIGNALED(status)) { 342 warnx("%s: %s", spec, strsignal(WTERMSIG(status))); 343 return (1); 344 } 345 break; 346 } 347 348 return (0); 349 } 350 351 352 static int 353 selected(const char *type) 354 { 355 struct entry *e; 356 357 /* If no type specified, it's always selected. */ 358 TAILQ_FOREACH(e, &selhead, entries) 359 if (!strncmp(e->type, type, MFSNAMELEN)) 360 return which == IN_LIST ? 1 : 0; 361 362 return which == IN_LIST ? 0 : 1; 363 } 364 365 366 static const char * 367 getoptions(const char *type) 368 { 369 struct entry *e; 370 371 TAILQ_FOREACH(e, &opthead, entries) 372 if (!strncmp(e->type, type, MFSNAMELEN)) 373 return e->options; 374 return ""; 375 } 376 377 378 static void 379 addoption(char *optstr) 380 { 381 char *newoptions; 382 struct entry *e; 383 384 if ((newoptions = strchr(optstr, ':')) == NULL) 385 errx(1, "Invalid option string"); 386 387 *newoptions++ = '\0'; 388 389 TAILQ_FOREACH(e, &opthead, entries) 390 if (!strncmp(e->type, optstr, MFSNAMELEN)) { 391 e->options = catopt(e->options, newoptions, 1); 392 return; 393 } 394 addentry(&opthead, optstr, newoptions); 395 } 396 397 398 static void 399 addentry(struct fstypelist *list, const char *type, const char *opts) 400 { 401 struct entry *e; 402 403 e = emalloc(sizeof(struct entry)); 404 e->type = estrdup(type); 405 e->options = estrdup(opts); 406 TAILQ_INSERT_TAIL(list, e, entries); 407 } 408 409 410 static void 411 maketypelist(char *fslist) 412 { 413 char *ptr; 414 415 if ((fslist == NULL) || (fslist[0] == '\0')) 416 errx(1, "empty type list"); 417 418 if (fslist[0] == 'n' && fslist[1] == 'o') { 419 fslist += 2; 420 which = NOT_IN_LIST; 421 } 422 else 423 which = IN_LIST; 424 425 while ((ptr = strsep(&fslist, ",")) != NULL) 426 addentry(&selhead, ptr, ""); 427 428 } 429 430 431 static char * 432 catopt(char *s0, const char *s1, int fr) 433 { 434 char *cp; 435 436 if (s0 && *s0) { 437 if (asprintf(&cp, "%s,%s", s0, s1) == -1) 438 err(1, "malloc failed"); 439 } else 440 cp = estrdup(s1); 441 442 if (s0 && fr) 443 free(s0); 444 return (cp); 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 = *argcp, maxargc = *maxargcp; 453 const char **argv = *argvp; 454 455 argc = *argcp; 456 maxargc = *maxargcp; 457 458 for (s = opts; (p = strsep(&s, ",")) != NULL;) { 459 /* always leave space for one more argument and the NULL */ 460 if (argc >= maxargc - 3) { 461 int newmaxargc = maxargc + 50; 462 463 argv = ereallocarray(argv, newmaxargc, sizeof(char *)); 464 maxargc = newmaxargc; 465 } 466 if (*p != '\0') { 467 if (*p == '-') { 468 argv[argc++] = p; 469 p = strchr(p, '='); 470 if (p) { 471 *p = '\0'; 472 argv[argc++] = p+1; 473 } 474 } 475 else { 476 argv[argc++] = "-o"; 477 argv[argc++] = p; 478 } 479 } 480 } 481 482 *argcp = argc; 483 *argvp = argv; 484 *maxargcp = maxargc; 485 } 486 487 static int 488 hasopt(const char *mntopts, const char *option) 489 { 490 int found; 491 char *opt, *optbuf; 492 493 if (mntopts == NULL) 494 return (0); 495 optbuf = strdup(mntopts); 496 found = 0; 497 for (opt = optbuf; !found && opt != NULL; strsep(&opt, ",")) 498 found = !strncmp(opt, option, strlen(option)); 499 free(optbuf); 500 return (found); 501 } 502 503 504 static void 505 usage(void) 506 { 507 extern char *__progname; 508 509 fprintf(stderr, "usage: %s " 510 "[-dfNnpvy] [-b block#] [-l maxparallel] [-T fstype:fsoptions]\n" 511 " [-t fstype] [special | node ...]\n", __progname); 512 exit(1); 513 } 514