fsck.c revision 1.22
1/* $OpenBSD: fsck.c,v 1.22 2005/11/12 13:28:34 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 38static const char rcsid[] = "$OpenBSD: fsck.c,v 1.22 2005/11/12 13:28:34 deraadt Exp $"; 39 40#include <sys/param.h> 41#include <sys/mount.h> 42#include <sys/queue.h> 43#include <sys/resource.h> 44#include <sys/wait.h> 45 46#include <err.h> 47#include <errno.h> 48#include <fstab.h> 49#include <signal.h> 50#include <stdio.h> 51#include <stdlib.h> 52#include <string.h> 53#include <unistd.h> 54#include <util.h> 55 56#include "pathnames.h" 57#include "fsutil.h" 58 59static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST; 60 61TAILQ_HEAD(fstypelist, entry) opthead, selhead; 62 63struct entry { 64 char *type; 65 char *options; 66 TAILQ_ENTRY(entry) entries; 67}; 68 69static int maxrun = 0; 70static char *options = NULL; 71static int flags = 0; 72 73int main(int, char *[]); 74 75static int checkfs(const char *, const char *, const char *, void *, pid_t *); 76static int selected(const char *); 77static void addoption(char *); 78static const char *getoptions(const char *); 79static void addentry(struct fstypelist *, const char *, const char *); 80static void maketypelist(char *); 81static char *catopt(char *, const char *, int); 82static void mangle(char *, int *, const char ***, int *); 83static void usage(void); 84static void *isok(struct fstab *); 85 86 87int 88main(int argc, char *argv[]) 89{ 90 struct fstab *fs; 91 int i, rval = 0; 92 char *vfstype = NULL; 93 char globopt[3]; 94 struct rlimit rl; 95 96 /* Increase our data size to the max */ 97 if (getrlimit(RLIMIT_DATA, &rl) == 0) { 98 if (geteuid() == 0) 99 rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; 100 else 101 rl.rlim_cur = rl.rlim_max; 102 if (setrlimit(RLIMIT_DATA, &rl) < 0) 103 warn("Can't get resource limit to max data size"); 104 } else 105 warn("Can't get resource limit for data size"); 106 107 globopt[0] = '-'; 108 globopt[2] = '\0'; 109 110 TAILQ_INIT(&selhead); 111 TAILQ_INIT(&opthead); 112 113 while ((i = getopt(argc, argv, "dvpfnyl:t:T:")) != -1) 114 switch (i) { 115 case 'd': 116 flags |= CHECK_DEBUG; 117 break; 118 119 case 'v': 120 flags |= CHECK_VERBOSE; 121 break; 122 123 case 'p': 124 flags |= CHECK_PREEN; 125 /*FALLTHROUGH*/ 126 case 'n': 127 case 'f': 128 case 'y': 129 globopt[1] = i; 130 options = catopt(options, globopt, 1); 131 break; 132 133 case 'l': 134 maxrun = atoi(optarg); 135 break; 136 137 case 'T': 138 if (*optarg) 139 addoption(optarg); 140 break; 141 142 case 't': 143 if (!TAILQ_EMPTY(&selhead)) 144 errx(1, "only one -t option may be specified."); 145 146 maketypelist(optarg); 147 vfstype = optarg; 148 break; 149 150 case '?': 151 default: 152 usage(); 153 /* NOTREACHED */ 154 } 155 156 argc -= optind; 157 argv += optind; 158 159 if (argc == 0) 160 return checkfstab(flags, maxrun, isok, checkfs); 161 162#define BADTYPE(type) \ 163 (strcmp(type, FSTAB_RO) && \ 164 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) 165 166 167 for (; argc--; argv++) { 168 char *spec, *type; 169 170 if (strncmp(*argv, "/dev/", 5) == 0 && 171 (type = readlabelfs(*argv, 0))) { 172 spec = *argv; 173 } else if ((fs = getfsfile(*argv)) == NULL && 174 (fs = getfsspec(*argv)) == NULL) { 175 if (vfstype == NULL) 176 errx(1, 177 "%s: unknown special file or file system.", 178 *argv); 179 spec = *argv; 180 type = vfstype; 181 } else { 182 spec = fs->fs_spec; 183 type = fs->fs_vfstype; 184 if (BADTYPE(fs->fs_type)) 185 errx(1, "%s has unknown file system type.", 186 *argv); 187 } 188 189 rval |= checkfs(type, blockcheck(spec), *argv, NULL, NULL); 190 } 191 192 return rval; 193} 194 195 196static void * 197isok(struct fstab *fs) 198{ 199 if (fs->fs_passno == 0) 200 return NULL; 201 202 if (BADTYPE(fs->fs_type)) 203 return NULL; 204 205 if (!selected(fs->fs_vfstype)) 206 return NULL; 207 208 return fs; 209} 210 211 212static int 213checkfs(const char *vfstype, const char *spec, const char *mntpt, void *auxarg, 214 pid_t *pidp) 215{ 216 /* List of directories containing fsck_xxx subcommands. */ 217 static const char *edirs[] = { 218 _PATH_SBIN, 219 _PATH_USRSBIN, 220 NULL 221 }; 222 const char **argv, **edir; 223 pid_t pid; 224 int argc, i, status, maxargc; 225 char *optbuf = NULL, fsname[MAXPATHLEN], execname[MAXPATHLEN]; 226 const char *extra = getoptions(vfstype); 227 228 if (strcmp(vfstype, "ufs") == 0) 229 vfstype = MOUNT_UFS; 230 231 maxargc = 100; 232 argv = emalloc(sizeof(char *) * maxargc); 233 234 argc = 0; 235 (void)snprintf(fsname, sizeof(fsname), "fsck_%s", vfstype); 236 argv[argc++] = fsname; 237 238 if (options) { 239 if (extra != NULL) 240 optbuf = catopt(options, extra, 0); 241 else 242 optbuf = estrdup(options); 243 } 244 else if (extra) 245 optbuf = estrdup(extra); 246 247 if (optbuf) 248 mangle(optbuf, &argc, &argv, &maxargc); 249 250 argv[argc++] = spec; 251 argv[argc] = NULL; 252 253 if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) { 254 (void)printf("start %s %swait %s", mntpt, 255 pidp ? "no" : "", fsname); 256 for (i = 1; i < argc; i++) 257 (void)printf(" %s", argv[i]); 258 (void)printf("\n"); 259 } 260 261 switch (pid = fork()) { 262 case -1: /* Error. */ 263 warn("fork"); 264 if (optbuf) 265 free(optbuf); 266 return (1); 267 268 case 0: /* Child. */ 269 if (flags & CHECK_DEBUG) 270 _exit(0); 271 272 /* Go find an executable. */ 273 edir = edirs; 274 do { 275 (void)snprintf(execname, 276 sizeof(execname), "%s/fsck_%s", *edir, vfstype); 277 execv(execname, (char * const *)argv); 278 if (errno != ENOENT) { 279 if (spec) 280 warn("exec %s for %s", execname, spec); 281 else 282 warn("exec %s", execname); 283 } 284 } while (*++edir != NULL); 285 286 if (errno == ENOENT) { 287 if (spec) 288 warn("exec %s for %s", execname, spec); 289 else 290 warn("exec %s", execname); 291 } 292 exit(1); 293 /* NOTREACHED */ 294 295 default: /* Parent. */ 296 if (optbuf) 297 free(optbuf); 298 299 if (pidp) { 300 *pidp = pid; 301 return 0; 302 } 303 304 if (waitpid(pid, &status, 0) < 0) { 305 warn("waitpid"); 306 return (1); 307 } 308 309 if (WIFEXITED(status)) { 310 if (WEXITSTATUS(status) != 0) 311 return (WEXITSTATUS(status)); 312 } 313 else if (WIFSIGNALED(status)) { 314 warnx("%s: %s", spec, strsignal(WTERMSIG(status))); 315 return (1); 316 } 317 break; 318 } 319 320 return (0); 321} 322 323 324static int 325selected(const char *type) 326{ 327 struct entry *e; 328 329 /* If no type specified, it's always selected. */ 330 TAILQ_FOREACH(e, &selhead, entries) 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 338static const char * 339getoptions(const char *type) 340{ 341 struct entry *e; 342 343 TAILQ_FOREACH(e, &opthead, entries) 344 if (!strncmp(e->type, type, MFSNAMELEN)) 345 return e->options; 346 return ""; 347} 348 349 350static void 351addoption(char *optstr) 352{ 353 char *newoptions; 354 struct entry *e; 355 356 if ((newoptions = strchr(optstr, ':')) == NULL) 357 errx(1, "Invalid option string"); 358 359 *newoptions++ = '\0'; 360 361 TAILQ_FOREACH(e, &opthead, entries) 362 if (!strncmp(e->type, optstr, MFSNAMELEN)) { 363 e->options = catopt(e->options, newoptions, 1); 364 return; 365 } 366 addentry(&opthead, optstr, newoptions); 367} 368 369 370static void 371addentry(struct fstypelist *list, const char *type, const char *opts) 372{ 373 struct entry *e; 374 375 e = emalloc(sizeof(struct entry)); 376 e->type = estrdup(type); 377 e->options = estrdup(opts); 378 TAILQ_INSERT_TAIL(list, e, entries); 379} 380 381 382static void 383maketypelist(char *fslist) 384{ 385 char *ptr; 386 387 if ((fslist == NULL) || (fslist[0] == '\0')) 388 errx(1, "empty type list"); 389 390 if (fslist[0] == 'n' && fslist[1] == 'o') { 391 fslist += 2; 392 which = NOT_IN_LIST; 393 } 394 else 395 which = IN_LIST; 396 397 while ((ptr = strsep(&fslist, ",")) != NULL) 398 addentry(&selhead, ptr, ""); 399 400} 401 402 403static char * 404catopt(char *s0, const char *s1, int fr) 405{ 406 char *cp; 407 408 if (s0 && *s0) { 409 if (asprintf(&cp, "%s,%s", s0, s1) == -1) 410 err(1, "malloc failed"); 411 } else 412 cp = estrdup(s1); 413 414 if (s0 && fr) 415 free(s0); 416 return (cp); 417} 418 419 420static void 421mangle(char *opts, int *argcp, const char ***argvp, int *maxargcp) 422{ 423 char *p, *s; 424 int argc = *argcp, maxargc = *maxargcp; 425 const char **argv = *argvp; 426 427 argc = *argcp; 428 maxargc = *maxargcp; 429 430 for (s = opts; (p = strsep(&s, ",")) != NULL;) { 431 /* always leave space for one more argument and the NULL */ 432 if (argc >= maxargc - 3) { 433 int newmaxargc = maxargc + 50; 434 435 argv = erealloc(argv, newmaxargc * sizeof(char *)); 436 maxargc = newmaxargc; 437 } 438 if (*p != '\0') { 439 if (*p == '-') { 440 argv[argc++] = p; 441 p = strchr(p, '='); 442 if (p) { 443 *p = '\0'; 444 argv[argc++] = p+1; 445 } 446 } 447 else { 448 argv[argc++] = "-o"; 449 argv[argc++] = p; 450 } 451 } 452 } 453 454 *argcp = argc; 455 *argvp = argv; 456 *maxargcp = maxargc; 457} 458 459 460static void 461usage(void) 462{ 463 extern char *__progname; 464 465 fprintf(stderr, "usage: %s [-dfnpvy] [-l maxparallel] " 466 "[-T fstype:fsoptions] [-t fstype] [special | node ...]\n", 467 __progname); 468 exit(1); 469} 470