busybox/e2fsprogs/fsck.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * fsck --- A generic, parallelizing front-end for the fsck program.
   4 * It will automatically try to run fsck programs in parallel if the
   5 * devices are on separate spindles.  It is based on the same ideas as
   6 * the generic front end for fsck by David Engel and Fred van Kempen,
   7 * but it has been completely rewritten from scratch to support
   8 * parallel execution.
   9 *
  10 * Written by Theodore Ts'o, <tytso@mit.edu>
  11 *
  12 * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
  13 *   o Changed -t fstype to behave like with mount when -A (all file
  14 *     systems) or -M (like mount) is specified.
  15 *   o fsck looks if it can find the fsck.type program to decide
  16 *     if it should ignore the fs type. This way more fsck programs
  17 *     can be added without changing this front-end.
  18 *   o -R flag skip root file system.
  19 *
  20 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
  21 *      2001, 2002, 2003, 2004, 2005 by  Theodore Ts'o.
  22 *
  23 * Licensed under GPLv2, see file LICENSE in this source tree.
  24 */
  25
  26/* All filesystem specific hooks have been removed.
  27 * If filesystem cannot be determined, we will execute
  28 * "fsck.auto". Currently this also happens if you specify
  29 * UUID=xxx or LABEL=xxx as an object to check.
  30 * Detection code for that is also probably has to be in fsck.auto.
  31 *
  32 * In other words, this is _really_ is just a driver program which
  33 * spawns actual fsck.something for each filesystem to check.
  34 * It doesn't guess filesystem types from on-disk format.
  35 */
  36
  37#include "libbb.h"
  38
  39/* "progress indicator" code is somewhat buggy and ext[23] specific.
  40 * We should be filesystem agnostic. IOW: there should be a well-defined
  41 * API for fsck.something, NOT ad-hoc hacks in generic fsck. */
  42#define DO_PROGRESS_INDICATOR 0
  43
  44/* fsck 1.41.4 (27-Jan-2009) manpage says:
  45 * 0   - No errors
  46 * 1   - File system errors corrected
  47 * 2   - System should be rebooted
  48 * 4   - File system errors left uncorrected
  49 * 8   - Operational error
  50 * 16  - Usage or syntax error
  51 * 32  - Fsck canceled by user request
  52 * 128 - Shared library error
  53 */
  54#define EXIT_OK          0
  55#define EXIT_NONDESTRUCT 1
  56#define EXIT_DESTRUCT    2
  57#define EXIT_UNCORRECTED 4
  58#define EXIT_ERROR       8
  59#define EXIT_USAGE       16
  60#define FSCK_CANCELED    32     /* Aborted with a signal or ^C */
  61
  62/*
  63 * Internal structure for mount table entries.
  64 */
  65struct fs_info {
  66        struct fs_info *next;
  67        char    *device;
  68        char    *mountpt;
  69        char    *type;
  70        char    *opts;
  71        int     passno;
  72        int     flags;
  73};
  74
  75#define FLAG_DONE 1
  76#define FLAG_PROGRESS 2
  77/*
  78 * Structure to allow exit codes to be stored
  79 */
  80struct fsck_instance {
  81        struct fsck_instance *next;
  82        int     pid;
  83        int     flags;
  84#if DO_PROGRESS_INDICATOR
  85        time_t  start_time;
  86#endif
  87        char    *prog;
  88        char    *device;
  89        char    *base_device; /* /dev/hda for /dev/hdaN etc */
  90};
  91
  92static const char ignored_types[] ALIGN1 =
  93        "ignore\0"
  94        "iso9660\0"
  95        "nfs\0"
  96        "proc\0"
  97        "sw\0"
  98        "swap\0"
  99        "tmpfs\0"
 100        "devpts\0";
 101
 102#if 0
 103static const char really_wanted[] ALIGN1 =
 104        "minix\0"
 105        "ext2\0"
 106        "ext3\0"
 107        "jfs\0"
 108        "reiserfs\0"
 109        "xiafs\0"
 110        "xfs\0";
 111#endif
 112
 113#define BASE_MD "/dev/md"
 114
 115static char **args;
 116static int num_args;
 117static int verbose;
 118
 119#define FS_TYPE_FLAG_NORMAL 0
 120#define FS_TYPE_FLAG_OPT    1
 121#define FS_TYPE_FLAG_NEGOPT 2
 122static char **fs_type_list;
 123static uint8_t *fs_type_flag;
 124static smallint fs_type_negated;
 125
 126static smallint noexecute;
 127static smallint serialize;
 128static smallint skip_root;
 129/* static smallint like_mount; */
 130static smallint parallel_root;
 131static smallint force_all_parallel;
 132
 133#if DO_PROGRESS_INDICATOR
 134static smallint progress;
 135static int progress_fd;
 136#endif
 137
 138static int num_running;
 139static int max_running;
 140static char *fstype;
 141static struct fs_info *filesys_info;
 142static struct fs_info *filesys_last;
 143static struct fsck_instance *instance_list;
 144
 145/*
 146 * Return the "base device" given a particular device; this is used to
 147 * assure that we only fsck one partition on a particular drive at any
 148 * one time.  Otherwise, the disk heads will be seeking all over the
 149 * place.  If the base device cannot be determined, return NULL.
 150 *
 151 * The base_device() function returns an allocated string which must
 152 * be freed.
 153 */
 154#if ENABLE_FEATURE_DEVFS
 155/*
 156 * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3
 157 * pathames.
 158 */
 159static const char *const devfs_hier[] = {
 160        "host", "bus", "target", "lun", NULL
 161};
 162#endif
 163
 164static char *base_device(const char *device)
 165{
 166        char *str, *cp;
 167#if ENABLE_FEATURE_DEVFS
 168        const char *const *hier;
 169        const char *disk;
 170        int len;
 171#endif
 172        str = xstrdup(device);
 173
 174        /* Skip over "/dev/"; if it's not present, give up */
 175        cp = skip_dev_pfx(str);
 176        if (cp == str)
 177                goto errout;
 178
 179        /*
 180         * For md devices, we treat them all as if they were all
 181         * on one disk, since we don't know how to parallelize them.
 182         */
 183        if (cp[0] == 'm' && cp[1] == 'd') {
 184                cp[2] = 0;
 185                return str;
 186        }
 187
 188        /* Handle DAC 960 devices */
 189        if (strncmp(cp, "rd/", 3) == 0) {
 190                cp += 3;
 191                if (cp[0] != 'c' || !isdigit(cp[1])
 192                 || cp[2] != 'd' || !isdigit(cp[3]))
 193                        goto errout;
 194                cp[4] = 0;
 195                return str;
 196        }
 197
 198        /* Now let's handle /dev/hd* and /dev/sd* devices.... */
 199        if ((cp[0] == 'h' || cp[0] == 's') && cp[1] == 'd') {
 200                cp += 2;
 201                /* If there's a single number after /dev/hd, skip it */
 202                if (isdigit(*cp))
 203                        cp++;
 204                /* What follows must be an alpha char, or give up */
 205                if (!isalpha(*cp))
 206                        goto errout;
 207                cp[1] = 0;
 208                return str;
 209        }
 210
 211#if ENABLE_FEATURE_DEVFS
 212        /* Now let's handle devfs (ugh) names */
 213        len = 0;
 214        if (strncmp(cp, "ide/", 4) == 0)
 215                len = 4;
 216        if (strncmp(cp, "scsi/", 5) == 0)
 217                len = 5;
 218        if (len) {
 219                cp += len;
 220                /*
 221                 * Now we proceed down the expected devfs hierarchy.
 222                 * i.e., .../host1/bus2/target3/lun4/...
 223                 * If we don't find the expected token, followed by
 224                 * some number of digits at each level, abort.
 225                 */
 226                for (hier = devfs_hier; *hier; hier++) {
 227                        len = strlen(*hier);
 228                        if (strncmp(cp, *hier, len) != 0)
 229                                goto errout;
 230                        cp += len;
 231                        while (*cp != '/' && *cp != 0) {
 232                                if (!isdigit(*cp))
 233                                        goto errout;
 234                                cp++;
 235                        }
 236                        cp++;
 237                }
 238                cp[-1] = 0;
 239                return str;
 240        }
 241
 242        /* Now handle devfs /dev/disc or /dev/disk names */
 243        disk = 0;
 244        if (strncmp(cp, "discs/", 6) == 0)
 245                disk = "disc";
 246        else if (strncmp(cp, "disks/", 6) == 0)
 247                disk = "disk";
 248        if (disk) {
 249                cp += 6;
 250                if (strncmp(cp, disk, 4) != 0)
 251                        goto errout;
 252                cp += 4;
 253                while (*cp != '/' && *cp != 0) {
 254                        if (!isdigit(*cp))
 255                                goto errout;
 256                        cp++;
 257                }
 258                *cp = 0;
 259                return str;
 260        }
 261#endif
 262 errout:
 263        free(str);
 264        return NULL;
 265}
 266
 267static void free_instance(struct fsck_instance *p)
 268{
 269        free(p->prog);
 270        free(p->device);
 271        free(p->base_device);
 272        free(p);
 273}
 274
 275static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
 276                                        const char *type, const char *opts,
 277                                        int passno)
 278{
 279        struct fs_info *fs;
 280
 281        fs = xzalloc(sizeof(*fs));
 282        fs->device = xstrdup(device);
 283        fs->mountpt = xstrdup(mntpnt);
 284        if (strchr(type, ','))
 285                type = (char *)"auto";
 286        fs->type = xstrdup(type);
 287        fs->opts = xstrdup(opts ? opts : "");
 288        fs->passno = passno < 0 ? 1 : passno;
 289        /*fs->flags = 0; */
 290        /*fs->next = NULL; */
 291
 292        if (!filesys_info)
 293                filesys_info = fs;
 294        else
 295                filesys_last->next = fs;
 296        filesys_last = fs;
 297
 298        return fs;
 299}
 300
 301/* Load the filesystem database from /etc/fstab */
 302static void load_fs_info(const char *filename)
 303{
 304        FILE *fstab;
 305        struct mntent mte;
 306        struct fs_info *fs;
 307
 308        fstab = setmntent(filename, "r");
 309        if (!fstab) {
 310                bb_perror_msg("can't read '%s'", filename);
 311                return;
 312        }
 313
 314        // Loop through entries
 315        while (getmntent_r(fstab, &mte, bb_common_bufsiz1, COMMON_BUFSIZE)) {
 316                //bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir,
 317                //      mte.mnt_type, mte.mnt_opts,
 318                //      mte.mnt_passno);
 319                fs = create_fs_device(mte.mnt_fsname, mte.mnt_dir,
 320                        mte.mnt_type, mte.mnt_opts,
 321                        mte.mnt_passno);
 322        }
 323        endmntent(fstab);
 324}
 325
 326/* Lookup filesys in /etc/fstab and return the corresponding entry. */
 327static struct fs_info *lookup(char *filesys)
 328{
 329        struct fs_info *fs;
 330
 331        for (fs = filesys_info; fs; fs = fs->next) {
 332                if (strcmp(filesys, fs->device) == 0
 333                 || (fs->mountpt && strcmp(filesys, fs->mountpt) == 0)
 334                )
 335                        break;
 336        }
 337
 338        return fs;
 339}
 340
 341#if DO_PROGRESS_INDICATOR
 342static int progress_active(void)
 343{
 344        struct fsck_instance *inst;
 345
 346        for (inst = instance_list; inst; inst = inst->next) {
 347                if (inst->flags & FLAG_DONE)
 348                        continue;
 349                if (inst->flags & FLAG_PROGRESS)
 350                        return 1;
 351        }
 352        return 0;
 353}
 354#endif
 355
 356
 357/*
 358 * Send a signal to all outstanding fsck child processes
 359 */
 360static void kill_all_if_got_signal(void)
 361{
 362        static smallint kill_sent;
 363
 364        struct fsck_instance *inst;
 365
 366        if (!bb_got_signal || kill_sent)
 367                return;
 368
 369        for (inst = instance_list; inst; inst = inst->next) {
 370                if (inst->flags & FLAG_DONE)
 371                        continue;
 372                kill(inst->pid, SIGTERM);
 373        }
 374        kill_sent = 1;
 375}
 376
 377/*
 378 * Wait for one child process to exit; when it does, unlink it from
 379 * the list of executing child processes, free, and return its exit status.
 380 * If there is no exited child, return -1.
 381 */
 382static int wait_one(int flags)
 383{
 384        int status;
 385        int sig;
 386        struct fsck_instance *inst, *prev;
 387        pid_t pid;
 388
 389        if (!instance_list)
 390                return -1;
 391        /* if (noexecute) { already returned -1; } */
 392
 393        while (1) {
 394                pid = waitpid(-1, &status, flags);
 395                kill_all_if_got_signal();
 396                if (pid == 0) /* flags == WNOHANG and no children exited */
 397                        return -1;
 398                if (pid < 0) {
 399                        if (errno == EINTR)
 400                                continue;
 401                        if (errno == ECHILD) { /* paranoia */
 402                                bb_error_msg("wait: no more children");
 403                                return -1;
 404                        }
 405                        bb_perror_msg("wait");
 406                        continue;
 407                }
 408                prev = NULL;
 409                inst = instance_list;
 410                do {
 411                        if (inst->pid == pid)
 412                                goto child_died;
 413                        prev = inst;
 414                        inst = inst->next;
 415                } while (inst);
 416        }
 417 child_died:
 418
 419        if (WIFEXITED(status))
 420                status = WEXITSTATUS(status);
 421        else if (WIFSIGNALED(status)) {
 422                sig = WTERMSIG(status);
 423                status = EXIT_UNCORRECTED;
 424                if (sig != SIGINT) {
 425                        printf("Warning: %s %s terminated "
 426                                "by signal %d\n",
 427                                inst->prog, inst->device, sig);
 428                        status = EXIT_ERROR;
 429                }
 430        } else {
 431                printf("%s %s: status is %x, should never happen\n",
 432                        inst->prog, inst->device, status);
 433                status = EXIT_ERROR;
 434        }
 435
 436#if DO_PROGRESS_INDICATOR
 437        if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) {
 438                struct fsck_instance *inst2;
 439                for (inst2 = instance_list; inst2; inst2 = inst2->next) {
 440                        if (inst2->flags & FLAG_DONE)
 441                                continue;
 442                        if (strcmp(inst2->type, "ext2") != 0
 443                         && strcmp(inst2->type, "ext3") != 0
 444                        ) {
 445                                continue;
 446                        }
 447                        /* ext[23], we will send USR1
 448                         * (request to start displaying progress bar)
 449                         *
 450                         * If we've just started the fsck, wait a tiny
 451                         * bit before sending the kill, to give it
 452                         * time to set up the signal handler
 453                         */
 454                        if (inst2->start_time >= time(NULL) - 1)
 455                                sleep(1);
 456                        kill(inst2->pid, SIGUSR1);
 457                        inst2->flags |= FLAG_PROGRESS;
 458                        break;
 459                }
 460        }
 461#endif
 462
 463        if (prev)
 464                prev->next = inst->next;
 465        else
 466                instance_list = inst->next;
 467        if (verbose > 1)
 468                printf("Finished with %s (exit status %d)\n",
 469                       inst->device, status);
 470        num_running--;
 471        free_instance(inst);
 472
 473        return status;
 474}
 475
 476/*
 477 * Wait until all executing child processes have exited; return the
 478 * logical OR of all of their exit code values.
 479 */
 480#define FLAG_WAIT_ALL           0
 481#define FLAG_WAIT_ATLEAST_ONE   WNOHANG
 482static int wait_many(int flags)
 483{
 484        int exit_status;
 485        int global_status = 0;
 486        int wait_flags = 0;
 487
 488        while ((exit_status = wait_one(wait_flags)) != -1) {
 489                global_status |= exit_status;
 490                wait_flags |= flags;
 491        }
 492        return global_status;
 493}
 494
 495/*
 496 * Execute a particular fsck program, and link it into the list of
 497 * child processes we are waiting for.
 498 */
 499static void execute(const char *type, const char *device,
 500                const char *mntpt /*, int interactive */)
 501{
 502        int i;
 503        struct fsck_instance *inst;
 504        pid_t pid;
 505
 506        args[0] = xasprintf("fsck.%s", type);
 507
 508#if DO_PROGRESS_INDICATOR
 509        if (progress && !progress_active()) {
 510                if (strcmp(type, "ext2") == 0
 511                 || strcmp(type, "ext3") == 0
 512                ) {
 513                        args[XXX] = xasprintf("-C%d", progress_fd); /* 1 */
 514                        inst->flags |= FLAG_PROGRESS;
 515                }
 516        }
 517#endif
 518
 519        args[num_args - 2] = (char*)device;
 520        /* args[num_args - 1] = NULL; - already is */
 521
 522        if (verbose || noexecute) {
 523                printf("[%s (%d) -- %s]", args[0], num_running,
 524                                        mntpt ? mntpt : device);
 525                for (i = 0; args[i]; i++)
 526                        printf(" %s", args[i]);
 527                bb_putchar('\n');
 528        }
 529
 530        /* Fork and execute the correct program. */
 531        pid = -1;
 532        if (!noexecute) {
 533                pid = spawn(args);
 534                if (pid < 0)
 535                        bb_simple_perror_msg(args[0]);
 536        }
 537
 538#if DO_PROGRESS_INDICATOR
 539        free(args[XXX]);
 540#endif
 541
 542        /* No child, so don't record an instance */
 543        if (pid <= 0) {
 544                free(args[0]);
 545                return;
 546        }
 547
 548        inst = xzalloc(sizeof(*inst));
 549        inst->pid = pid;
 550        inst->prog = args[0];
 551        inst->device = xstrdup(device);
 552        inst->base_device = base_device(device);
 553#if DO_PROGRESS_INDICATOR
 554        inst->start_time = time(NULL);
 555#endif
 556
 557        /* Add to the list of running fsck's.
 558         * (was adding to the end, but adding to the front is simpler...) */
 559        inst->next = instance_list;
 560        instance_list = inst;
 561}
 562
 563/*
 564 * Run the fsck program on a particular device
 565 *
 566 * If the type is specified using -t, and it isn't prefixed with "no"
 567 * (as in "noext2") and only one filesystem type is specified, then
 568 * use that type regardless of what is specified in /etc/fstab.
 569 *
 570 * If the type isn't specified by the user, then use either the type
 571 * specified in /etc/fstab, or "auto".
 572 */
 573static void fsck_device(struct fs_info *fs /*, int interactive */)
 574{
 575        const char *type;
 576
 577        if (strcmp(fs->type, "auto") != 0) {
 578                type = fs->type;
 579                if (verbose > 2)
 580                        bb_info_msg("using filesystem type '%s' %s",
 581                                        type, "from fstab");
 582        } else if (fstype
 583         && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */
 584         && strncmp(fstype, "opts=", 5) != 0
 585         && strncmp(fstype, "loop", 4) != 0
 586         && !strchr(fstype, ',')
 587        ) {
 588                type = fstype;
 589                if (verbose > 2)
 590                        bb_info_msg("using filesystem type '%s' %s",
 591                                        type, "from -t");
 592        } else {
 593                type = "auto";
 594                if (verbose > 2)
 595                        bb_info_msg("using filesystem type '%s' %s",
 596                                        type, "(default)");
 597        }
 598
 599        num_running++;
 600        execute(type, fs->device, fs->mountpt /*, interactive */);
 601}
 602
 603/*
 604 * Returns TRUE if a partition on the same disk is already being
 605 * checked.
 606 */
 607static int device_already_active(char *device)
 608{
 609        struct fsck_instance *inst;
 610        char *base;
 611
 612        if (force_all_parallel)
 613                return 0;
 614
 615#ifdef BASE_MD
 616        /* Don't check a soft raid disk with any other disk */
 617        if (instance_list
 618         && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1)
 619             || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1))
 620        ) {
 621                return 1;
 622        }
 623#endif
 624
 625        base = base_device(device);
 626        /*
 627         * If we don't know the base device, assume that the device is
 628         * already active if there are any fsck instances running.
 629         */
 630        if (!base)
 631                return (instance_list != NULL);
 632
 633        for (inst = instance_list; inst; inst = inst->next) {
 634                if (!inst->base_device || !strcmp(base, inst->base_device)) {
 635                        free(base);
 636                        return 1;
 637                }
 638        }
 639
 640        free(base);
 641        return 0;
 642}
 643
 644/*
 645 * This function returns true if a particular option appears in a
 646 * comma-delimited options list
 647 */
 648static int opt_in_list(char *opt, char *optlist)
 649{
 650        char *s;
 651        int len;
 652
 653        if (!optlist)
 654                return 0;
 655
 656        len = strlen(opt);
 657        s = optlist - 1;
 658        while (1) {
 659                s = strstr(s + 1, opt);
 660                if (!s)
 661                        return 0;
 662                /* neither "opt.." nor "xxx,opt.."? */
 663                if (s != optlist && s[-1] != ',')
 664                        continue;
 665                /* neither "..opt" nor "..opt,xxx"? */
 666                if (s[len] != '\0' && s[len] != ',')
 667                        continue;
 668                return 1;
 669        }
 670}
 671
 672/* See if the filesystem matches the criteria given by the -t option */
 673static int fs_match(struct fs_info *fs)
 674{
 675        int n, ret, checked_type;
 676        char *cp;
 677
 678        if (!fs_type_list)
 679                return 1;
 680
 681        ret = 0;
 682        checked_type = 0;
 683        n = 0;
 684        while (1) {
 685                cp = fs_type_list[n];
 686                if (!cp)
 687                        break;
 688                switch (fs_type_flag[n]) {
 689                case FS_TYPE_FLAG_NORMAL:
 690                        checked_type++;
 691                        if (strcmp(cp, fs->type) == 0)
 692                                ret = 1;
 693                        break;
 694                case FS_TYPE_FLAG_NEGOPT:
 695                        if (opt_in_list(cp, fs->opts))
 696                                return 0;
 697                        break;
 698                case FS_TYPE_FLAG_OPT:
 699                        if (!opt_in_list(cp, fs->opts))
 700                                return 0;
 701                        break;
 702                }
 703                n++;
 704        }
 705        if (checked_type == 0)
 706                return 1;
 707
 708        return (fs_type_negated ? !ret : ret);
 709}
 710
 711/* Check if we should ignore this filesystem. */
 712static int ignore(struct fs_info *fs)
 713{
 714        /*
 715         * If the pass number is 0, ignore it.
 716         */
 717        if (fs->passno == 0)
 718                return 1;
 719
 720        /*
 721         * If a specific fstype is specified, and it doesn't match,
 722         * ignore it.
 723         */
 724        if (!fs_match(fs))
 725                return 1;
 726
 727        /* Are we ignoring this type? */
 728        if (index_in_strings(ignored_types, fs->type) >= 0)
 729                return 1;
 730
 731        /* We can and want to check this file system type. */
 732        return 0;
 733}
 734
 735/* Check all file systems, using the /etc/fstab table. */
 736static int check_all(void)
 737{
 738        struct fs_info *fs;
 739        int status = EXIT_OK;
 740        smallint not_done_yet;
 741        smallint pass_done;
 742        int passno;
 743
 744        if (verbose)
 745                puts("Checking all filesystems");
 746
 747        /*
 748         * Do an initial scan over the filesystem; mark filesystems
 749         * which should be ignored as done, and resolve any "auto"
 750         * filesystem types (done as a side-effect of calling ignore()).
 751         */
 752        for (fs = filesys_info; fs; fs = fs->next)
 753                if (ignore(fs))
 754                        fs->flags |= FLAG_DONE;
 755
 756        /*
 757         * Find and check the root filesystem.
 758         */
 759        if (!parallel_root) {
 760                for (fs = filesys_info; fs; fs = fs->next) {
 761                        if (LONE_CHAR(fs->mountpt, '/')) {
 762                                if (!skip_root && !ignore(fs)) {
 763                                        fsck_device(fs /*, 1*/);
 764                                        status |= wait_many(FLAG_WAIT_ALL);
 765                                        if (status > EXIT_NONDESTRUCT)
 766                                                return status;
 767                                }
 768                                fs->flags |= FLAG_DONE;
 769                                break;
 770                        }
 771                }
 772        }
 773        /*
 774         * This is for the bone-headed user who has root
 775         * filesystem listed twice.
 776         * "Skip root" will skip _all_ root entries.
 777         */
 778        if (skip_root)
 779                for (fs = filesys_info; fs; fs = fs->next)
 780                        if (LONE_CHAR(fs->mountpt, '/'))
 781                                fs->flags |= FLAG_DONE;
 782
 783        not_done_yet = 1;
 784        passno = 1;
 785        while (not_done_yet) {
 786                not_done_yet = 0;
 787                pass_done = 1;
 788
 789                for (fs = filesys_info; fs; fs = fs->next) {
 790                        if (bb_got_signal)
 791                                break;
 792                        if (fs->flags & FLAG_DONE)
 793                                continue;
 794                        /*
 795                         * If the filesystem's pass number is higher
 796                         * than the current pass number, then we didn't
 797                         * do it yet.
 798                         */
 799                        if (fs->passno > passno) {
 800                                not_done_yet = 1;
 801                                continue;
 802                        }
 803                        /*
 804                         * If a filesystem on a particular device has
 805                         * already been spawned, then we need to defer
 806                         * this to another pass.
 807                         */
 808                        if (device_already_active(fs->device)) {
 809                                pass_done = 0;
 810                                continue;
 811                        }
 812                        /*
 813                         * Spawn off the fsck process
 814                         */
 815                        fsck_device(fs /*, serialize*/);
 816                        fs->flags |= FLAG_DONE;
 817
 818                        /*
 819                         * Only do one filesystem at a time, or if we
 820                         * have a limit on the number of fsck's extant
 821                         * at one time, apply that limit.
 822                         */
 823                        if (serialize
 824                         || (max_running && (num_running >= max_running))
 825                        ) {
 826                                pass_done = 0;
 827                                break;
 828                        }
 829                }
 830                if (bb_got_signal)
 831                        break;
 832                if (verbose > 1)
 833                        printf("--waiting-- (pass %d)\n", passno);
 834                status |= wait_many(pass_done ? FLAG_WAIT_ALL :
 835                                    FLAG_WAIT_ATLEAST_ONE);
 836                if (pass_done) {
 837                        if (verbose > 1)
 838                                puts("----------------------------------");
 839                        passno++;
 840                } else
 841                        not_done_yet = 1;
 842        }
 843        kill_all_if_got_signal();
 844        status |= wait_many(FLAG_WAIT_ATLEAST_ONE);
 845        return status;
 846}
 847
 848/*
 849 * Deal with the fsck -t argument.
 850 * Huh, for mount "-t novfat,nfs" means "neither vfat nor nfs"!
 851 * Why here we require "-t novfat,nonfs" ??
 852 */
 853static void compile_fs_type(char *fs_type)
 854{
 855        char *s;
 856        int num = 2;
 857        smallint negate;
 858
 859        s = fs_type;
 860        while ((s = strchr(s, ','))) {
 861                num++;
 862                s++;
 863        }
 864
 865        fs_type_list = xzalloc(num * sizeof(fs_type_list[0]));
 866        fs_type_flag = xzalloc(num * sizeof(fs_type_flag[0]));
 867        fs_type_negated = -1; /* not yet known is it negated or not */
 868
 869        num = 0;
 870        s = fs_type;
 871        while (1) {
 872                char *comma;
 873
 874                negate = 0;
 875                if (s[0] == 'n' && s[1] == 'o') { /* "no.." */
 876                        s += 2;
 877                        negate = 1;
 878                } else if (s[0] == '!') {
 879                        s++;
 880                        negate = 1;
 881                }
 882
 883                if (strcmp(s, "loop") == 0)
 884                        /* loop is really short-hand for opts=loop */
 885                        goto loop_special_case;
 886                if (strncmp(s, "opts=", 5) == 0) {
 887                        s += 5;
 888 loop_special_case:
 889                        fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT;
 890                } else {
 891                        if (fs_type_negated == -1)
 892                                fs_type_negated = negate;
 893                        if (fs_type_negated != negate)
 894                                bb_error_msg_and_die(
 895"either all or none of the filesystem types passed to -t must be prefixed "
 896"with 'no' or '!'");
 897                }
 898                comma = strchr(s, ',');
 899                fs_type_list[num++] = comma ? xstrndup(s, comma-s) : xstrdup(s);
 900                if (!comma)
 901                        break;
 902                s = comma + 1;
 903        }
 904}
 905
 906static char **new_args(void)
 907{
 908        args = xrealloc_vector(args, 2, num_args);
 909        return &args[num_args++];
 910}
 911
 912int fsck_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 913int fsck_main(int argc UNUSED_PARAM, char **argv)
 914{
 915        int i, status;
 916        /*int interactive;*/
 917        struct fs_info *fs;
 918        const char *fstab;
 919        char *tmp;
 920        char **devices;
 921        int num_devices;
 922        smallint opts_for_fsck;
 923        smallint doall;
 924        smallint notitle;
 925
 926        /* we want wait() to be interruptible */
 927        signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
 928        signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo);
 929
 930        setbuf(stdout, NULL);
 931
 932        opts_for_fsck = doall = notitle = 0;
 933        devices = NULL;
 934        num_devices = 0;
 935        new_args(); /* args[0] = NULL, will be replaced by fsck.<type> */
 936        /* instance_list = NULL; - in bss, so already zeroed */
 937
 938        while (*++argv) {
 939                int j;
 940                int optpos;
 941                char *options;
 942                char *arg = *argv;
 943
 944                /* "/dev/blk" or "/path" or "UUID=xxx" or "LABEL=xxx" */
 945                if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
 946// FIXME: must check that arg is a blkdev, or resolve
 947// "/path", "UUID=xxx" or "LABEL=xxx" into block device name
 948// ("UUID=xxx"/"LABEL=xxx" can probably shifted to fsck.auto duties)
 949                        devices = xrealloc_vector(devices, 2, num_devices);
 950                        devices[num_devices++] = arg;
 951                        continue;
 952                }
 953
 954                if (arg[0] != '-' || opts_for_fsck) {
 955                        *new_args() = arg;
 956                        continue;
 957                }
 958
 959                if (LONE_CHAR(arg + 1, '-')) { /* "--" ? */
 960                        opts_for_fsck = 1;
 961                        continue;
 962                }
 963
 964                optpos = 0;
 965                options = NULL;
 966                for (j = 1; arg[j]; j++) {
 967                        switch (arg[j]) {
 968                        case 'A':
 969                                doall = 1;
 970                                break;
 971#if DO_PROGRESS_INDICATOR
 972                        case 'C':
 973                                progress = 1;
 974                                if (arg[++j]) { /* -Cn */
 975                                        progress_fd = xatoi_positive(&arg[j]);
 976                                        goto next_arg;
 977                                }
 978                                /* -C n */
 979                                if (!*++argv)
 980                                        bb_show_usage();
 981                                progress_fd = xatoi_positive(*argv);
 982                                goto next_arg;
 983#endif
 984                        case 'V':
 985                                verbose++;
 986                                break;
 987                        case 'N':
 988                                noexecute = 1;
 989                                break;
 990                        case 'R':
 991                                skip_root = 1;
 992                                break;
 993                        case 'T':
 994                                notitle = 1;
 995                                break;
 996/*                      case 'M':
 997                                like_mount = 1;
 998                                break; */
 999                        case 'P':
1000                                parallel_root = 1;
1001                                break;
1002                        case 's':
1003                                serialize = 1;
1004                                break;
1005                        case 't':
1006                                if (fstype)
1007                                        bb_show_usage();
1008                                if (arg[++j])
1009                                        tmp = &arg[j];
1010                                else if (*++argv)
1011                                        tmp = *argv;
1012                                else
1013                                        bb_show_usage();
1014                                fstype = xstrdup(tmp);
1015                                compile_fs_type(fstype);
1016                                goto next_arg;
1017                        case '?':
1018                                bb_show_usage();
1019                                break;
1020                        default:
1021                                optpos++;
1022                                /* one extra for '\0' */
1023                                options = xrealloc(options, optpos + 2);
1024                                options[optpos] = arg[j];
1025                                break;
1026                        }
1027                }
1028 next_arg:
1029                if (optpos) {
1030                        options[0] = '-';
1031                        options[optpos + 1] = '\0';
1032                        *new_args() = options;
1033                }
1034        }
1035        if (getenv("FSCK_FORCE_ALL_PARALLEL"))
1036                force_all_parallel = 1;
1037        tmp = getenv("FSCK_MAX_INST");
1038        if (tmp)
1039                max_running = xatoi(tmp);
1040        new_args(); /* args[num_args - 2] will be replaced by <device> */
1041        new_args(); /* args[num_args - 1] is the last, NULL element */
1042
1043        if (!notitle)
1044                puts("fsck (busybox "BB_VER", "BB_BT")");
1045
1046        /* Even plain "fsck /dev/hda1" needs fstab to get fs type,
1047         * so we are scanning it anyway */
1048        fstab = getenv("FSTAB_FILE");
1049        if (!fstab)
1050                fstab = "/etc/fstab";
1051        load_fs_info(fstab);
1052
1053        /*interactive = (num_devices == 1) | serialize;*/
1054
1055        if (num_devices == 0)
1056                /*interactive =*/ serialize = doall = 1;
1057        if (doall)
1058                return check_all();
1059
1060        status = 0;
1061        for (i = 0; i < num_devices; i++) {
1062                if (bb_got_signal) {
1063                        kill_all_if_got_signal();
1064                        break;
1065                }
1066
1067                fs = lookup(devices[i]);
1068                if (!fs)
1069                        fs = create_fs_device(devices[i], "", "auto", NULL, -1);
1070                fsck_device(fs /*, interactive */);
1071
1072                if (serialize
1073                 || (max_running && (num_running >= max_running))
1074                ) {
1075                        int exit_status = wait_one(0);
1076                        if (exit_status >= 0)
1077                                status |= exit_status;
1078                        if (verbose > 1)
1079                                puts("----------------------------------");
1080                }
1081        }
1082        status |= wait_many(FLAG_WAIT_ALL);
1083        return status;
1084}
1085