busybox/util-linux/fsck_minix.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * fsck.c - a file system consistency checker for Linux.
   4 *
   5 * (C) 1991, 1992 Linus Torvalds.
   6 *
   7 * Licensed under GPLv2, see file LICENSE in this source tree.
   8 */
   9
  10/*
  11 * 09.11.91  -  made the first rudimentary functions
  12 *
  13 * 10.11.91  -  updated, does checking, no repairs yet.
  14 *              Sent out to the mailing-list for testing.
  15 *
  16 * 14.11.91  -  Testing seems to have gone well. Added some
  17 *              correction-code, and changed some functions.
  18 *
  19 * 15.11.91  -  More correction code. Hopefully it notices most
  20 *              cases now, and tries to do something about them.
  21 *
  22 * 16.11.91  -  More corrections (thanks to Mika Jalava). Most
  23 *              things seem to work now. Yeah, sure.
  24 *
  25 * 19.04.92  -  Had to start over again from this old version, as a
  26 *              kernel bug ate my enhanced fsck in february.
  27 *
  28 * 28.02.93  -  added support for different directory entry sizes..
  29 *
  30 * Sat Mar  6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
  31 *                           superblock information
  32 *
  33 * Sat Oct  9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
  34 *                           to that required by fsutil
  35 *
  36 * Mon Jan  3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
  37 *                            Added support for file system valid flag.  Also
  38 *                            added program_version variable and output of
  39 *                            program name and version number when program
  40 *                            is executed.
  41 *
  42 * 30.10.94  - added support for v2 filesystem
  43 *             (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
  44 *
  45 * 10.12.94  - added test to prevent checking of mounted fs adapted
  46 *             from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
  47 *             program.  (Daniel Quinlan, quinlan@yggdrasil.com)
  48 *
  49 * 01.07.96  - Fixed the v2 fs stuff to use the right #defines and such
  50 *             for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
  51 *
  52 * 02.07.96  - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
  53 *             (Russell King).  He made them for ARM.  It would seem
  54 *             that the ARM is powerful enough to do this in C whereas
  55 *             i386 and m64k must use assembly to get it fast >:-)
  56 *             This should make minix fsck system-independent.
  57 *             (janl@math.uio.no, Nicolai Langfeldt)
  58 *
  59 * 04.11.96  - Added minor fixes from Andreas Schwab to avoid compiler
  60 *             warnings.  Added mc68k bitops from
  61 *             Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
  62 *
  63 * 06.11.96  - Added v2 code submitted by Joerg Dorchain, but written by
  64 *             Andreas Schwab.
  65 *
  66 * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
  67 * - added Native Language Support
  68 *
  69 *
  70 * I've had no time to add comments - hopefully the function names
  71 * are comments enough. As with all file system checkers, this assumes
  72 * the file system is quiescent - don't use it on a mounted device
  73 * unless you can be sure nobody is writing to it (and remember that the
  74 * kernel can write to it when it searches for files).
  75 *
  76 * Usage: fsck [-larvsm] device
  77 *      -l for a listing of all the filenames
  78 *      -a for automatic repairs (not implemented)
  79 *      -r for repairs (interactive) (not implemented)
  80 *      -v for verbose (tells how many files)
  81 *      -s for superblock info
  82 *      -m for minix-like "mode not cleared" warnings
  83 *      -f force filesystem check even if filesystem marked as valid
  84 *
  85 * The device may be a block device or a image of one, but this isn't
  86 * enforced (but it's not much fun on a character device :-).
  87 */
  88//config:config FSCK_MINIX
  89//config:       bool "fsck.minix (13 kb)"
  90//config:       default y
  91//config:       help
  92//config:       The minix filesystem is a nice, small, compact, read-write filesystem
  93//config:       with little overhead. It is not a journaling filesystem however and
  94//config:       can experience corruption if it is not properly unmounted or if the
  95//config:       power goes off in the middle of a write. This utility allows you to
  96//config:       check for and attempt to repair any corruption that occurs to a minix
  97//config:       filesystem.
  98
  99//applet:IF_FSCK_MINIX(APPLET_ODDNAME(fsck.minix, fsck_minix, BB_DIR_SBIN, BB_SUID_DROP, fsck_minix))
 100
 101//kbuild:lib-$(CONFIG_FSCK_MINIX) += fsck_minix.o
 102
 103//usage:#define fsck_minix_trivial_usage
 104//usage:       "[-larvsmf] BLOCKDEV"
 105//usage:#define fsck_minix_full_usage "\n\n"
 106//usage:       "Check MINIX filesystem\n"
 107//usage:     "\n        -l      List all filenames"
 108//usage:     "\n        -r      Perform interactive repairs"
 109//usage:     "\n        -a      Perform automatic repairs"
 110//usage:     "\n        -v      Verbose"
 111//usage:     "\n        -s      Output superblock information"
 112//usage:     "\n        -m      Show \"mode not cleared\" warnings"
 113//usage:     "\n        -f      Force file system check"
 114
 115#include <mntent.h>
 116#include "libbb.h"
 117#include "minix.h"
 118
 119#ifndef BLKGETSIZE
 120#define BLKGETSIZE _IO(0x12,96)    /* return device size */
 121#endif
 122
 123struct BUG_bad_inode_size {
 124        char BUG_bad_inode1_size[(INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1];
 125#if ENABLE_FEATURE_MINIX2
 126        char BUG_bad_inode2_size[(INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1];
 127#endif
 128};
 129
 130enum {
 131#ifdef UNUSED
 132        MINIX1_LINK_MAX = 250,
 133        MINIX2_LINK_MAX = 65530,
 134        MINIX_I_MAP_SLOTS = 8,
 135        MINIX_Z_MAP_SLOTS = 64,
 136        MINIX_V1 = 0x0001,      /* original minix fs */
 137        MINIX_V2 = 0x0002,      /* minix V2 fs */
 138#endif
 139        MINIX_NAME_MAX = 255,         /* # chars in a file name */
 140};
 141
 142
 143#if !ENABLE_FEATURE_MINIX2
 144enum { version2 = 0 };
 145#endif
 146
 147enum { MAX_DEPTH = 32 };
 148
 149enum { dev_fd = 3 };
 150
 151struct globals {
 152#if ENABLE_FEATURE_MINIX2
 153        smallint version2;
 154#endif
 155        smallint changed;  /* is filesystem modified? */
 156        smallint errors_uncorrected;  /* flag if some error was not corrected */
 157        smallint termios_set;
 158        smallint dirsize;
 159        smallint namelen;
 160        const char *device_name;
 161        int directory, regular, blockdev, chardev, links, symlinks, total;
 162        char *inode_buffer;
 163
 164        char *inode_map;
 165        char *zone_map;
 166
 167        unsigned char *inode_count;
 168        unsigned char *zone_count;
 169
 170        /* File-name data */
 171        int name_depth;
 172        char *name_component[MAX_DEPTH+1];
 173
 174        /* Bigger stuff */
 175        struct termios sv_termios;
 176        union {
 177                char superblock_buffer[BLOCK_SIZE];
 178                struct minix_superblock Super;
 179        } u;
 180        char add_zone_ind_blk[BLOCK_SIZE];
 181        char add_zone_dind_blk[BLOCK_SIZE];
 182        IF_FEATURE_MINIX2(char add_zone_tind_blk[BLOCK_SIZE];)
 183        char check_file_blk[BLOCK_SIZE];
 184
 185        /* File-name data */
 186        char current_name[MAX_DEPTH * MINIX_NAME_MAX];
 187};
 188#define G (*ptr_to_globals)
 189#if ENABLE_FEATURE_MINIX2
 190#define version2           (G.version2           )
 191#endif
 192#define changed            (G.changed            )
 193#define errors_uncorrected (G.errors_uncorrected )
 194#define termios_set        (G.termios_set        )
 195#define dirsize            (G.dirsize            )
 196#define namelen            (G.namelen            )
 197#define device_name        (G.device_name        )
 198#define directory          (G.directory          )
 199#define regular            (G.regular            )
 200#define blockdev           (G.blockdev           )
 201#define chardev            (G.chardev            )
 202#define links              (G.links              )
 203#define symlinks           (G.symlinks           )
 204#define total              (G.total              )
 205#define inode_buffer       (G.inode_buffer       )
 206#define inode_map          (G.inode_map          )
 207#define zone_map           (G.zone_map           )
 208#define inode_count        (G.inode_count        )
 209#define zone_count         (G.zone_count         )
 210#define name_depth         (G.name_depth         )
 211#define name_component     (G.name_component     )
 212#define sv_termios         (G.sv_termios         )
 213#define superblock_buffer  (G.u.superblock_buffer)
 214#define add_zone_ind_blk   (G.add_zone_ind_blk   )
 215#define add_zone_dind_blk  (G.add_zone_dind_blk  )
 216#define add_zone_tind_blk  (G.add_zone_tind_blk  )
 217#define check_file_blk     (G.check_file_blk     )
 218#define current_name       (G.current_name       )
 219#define INIT_G() do { \
 220        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
 221        dirsize = 16; \
 222        namelen = 14; \
 223        current_name[0] = '/'; \
 224        /*current_name[1] = '\0';*/ \
 225        name_component[0] = &current_name[0]; \
 226} while (0)
 227
 228
 229#define OPTION_STR "larvsmf"
 230enum {
 231        OPT_l = (1 << 0),
 232        OPT_a = (1 << 1),
 233        OPT_r = (1 << 2),
 234        OPT_v = (1 << 3),
 235        OPT_s = (1 << 4),
 236        OPT_w = (1 << 5),
 237        OPT_f = (1 << 6),
 238};
 239#define OPT_list      (option_mask32 & OPT_l)
 240#define OPT_automatic (option_mask32 & OPT_a)
 241#define OPT_repair    (option_mask32 & OPT_r)
 242#define OPT_verbose   (option_mask32 & OPT_v)
 243#define OPT_show      (option_mask32 & OPT_s)
 244#define OPT_warn_mode (option_mask32 & OPT_w)
 245#define OPT_force     (option_mask32 & OPT_f)
 246/* non-automatic repairs requested? */
 247#define OPT_manual    ((option_mask32 & (OPT_a|OPT_r)) == OPT_r)
 248
 249
 250#define Inode1 (((struct minix1_inode *) inode_buffer)-1)
 251#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
 252
 253#define Super (G.u.Super)
 254
 255#if ENABLE_FEATURE_MINIX2
 256# define ZONES    ((unsigned)(version2 ? Super.s_zones : Super.s_nzones))
 257#else
 258# define ZONES    ((unsigned)(Super.s_nzones))
 259#endif
 260#define INODES    ((unsigned)Super.s_ninodes)
 261#define IMAPS     ((unsigned)Super.s_imap_blocks)
 262#define ZMAPS     ((unsigned)Super.s_zmap_blocks)
 263#define FIRSTZONE ((unsigned)Super.s_firstdatazone)
 264#define ZONESIZE  ((unsigned)Super.s_log_zone_size)
 265#define MAXSIZE   ((unsigned)Super.s_max_size)
 266#define MAGIC     (Super.s_magic)
 267
 268/* gcc likes this more (code is smaller) than macro variant */
 269static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n)
 270{
 271        return (size + n-1) / n;
 272}
 273
 274#if !ENABLE_FEATURE_MINIX2
 275#define INODE_BLOCKS            div_roundup(INODES, MINIX1_INODES_PER_BLOCK)
 276#else
 277#define INODE_BLOCKS            div_roundup(INODES, \
 278                                (version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK))
 279#endif
 280
 281#define INODE_BUFFER_SIZE       (INODE_BLOCKS * BLOCK_SIZE)
 282#define NORM_FIRSTZONE          (2 + IMAPS + ZMAPS + INODE_BLOCKS)
 283
 284/* Before you ask "where they come from?": */
 285/* setbit/clrbit are supplied by sys/param.h */
 286
 287static int minix_bit(const char *a, unsigned i)
 288{
 289        return (a[i >> 3] & (1<<(i & 7)));
 290}
 291
 292static void minix_setbit(char *a, unsigned i)
 293{
 294        setbit(a, i);
 295        changed = 1;
 296}
 297static void minix_clrbit(char *a, unsigned i)
 298{
 299        clrbit(a, i);
 300        changed = 1;
 301}
 302
 303/* Note: do not assume 0/1, it is 0/nonzero */
 304#define zone_in_use(x)  (minix_bit(zone_map,(x)-FIRSTZONE+1))
 305#define inode_in_use(x) (minix_bit(inode_map,(x)))
 306
 307#define mark_inode(x)   (minix_setbit(inode_map,(x)))
 308#define unmark_inode(x) (minix_clrbit(inode_map,(x)))
 309
 310#define mark_zone(x)    (minix_setbit(zone_map,(x)-FIRSTZONE+1))
 311#define unmark_zone(x)  (minix_clrbit(zone_map,(x)-FIRSTZONE+1))
 312
 313
 314static void recursive_check(unsigned ino);
 315#if ENABLE_FEATURE_MINIX2
 316static void recursive_check2(unsigned ino);
 317#endif
 318
 319static void die(const char *str) NORETURN;
 320static void die(const char *str)
 321{
 322        if (termios_set)
 323                tcsetattr_stdin_TCSANOW(&sv_termios);
 324        bb_simple_error_msg_and_die(str);
 325}
 326
 327static void push_filename(const char *name)
 328{
 329        //  /dir/dir/dir/file
 330        //  ^   ^   ^
 331        // [0] [1] [2] <-name_component[i]
 332        if (name_depth < MAX_DEPTH) {
 333                int len;
 334                char *p = name_component[name_depth];
 335                *p++ = '/';
 336                len = sprintf(p, "%.*s", namelen, name);
 337                name_component[name_depth + 1] = p + len;
 338        }
 339        name_depth++;
 340}
 341
 342static void pop_filename(void)
 343{
 344        name_depth--;
 345        if (name_depth < MAX_DEPTH) {
 346                *name_component[name_depth] = '\0';
 347                if (!name_depth) {
 348                        current_name[0] = '/';
 349                        current_name[1] = '\0';
 350                }
 351        }
 352}
 353
 354static int ask(const char *string, int def)
 355{
 356        int c;
 357
 358        if (!OPT_repair) {
 359                bb_putchar('\n');
 360                errors_uncorrected = 1;
 361                return 0;
 362        }
 363        if (OPT_automatic) {
 364                bb_putchar('\n');
 365                if (!def)
 366                        errors_uncorrected = 1;
 367                return def;
 368        }
 369        printf(def ? "%s (y/n)? " : "%s (n/y)? ", string);
 370        for (;;) {
 371                fflush_all();
 372                c = getchar();
 373                if (c == EOF) {
 374                        if (!def)
 375                                errors_uncorrected = 1;
 376                        return def;
 377                }
 378                if (c == '\n')
 379                        break;
 380                c |= 0x20; /* tolower */
 381                if (c == 'y') {
 382                        def = 1;
 383                        break;
 384                }
 385                if (c == 'n') {
 386                        def = 0;
 387                        break;
 388                }
 389        }
 390        if (def)
 391                puts("y");
 392        else {
 393                puts("n");
 394                errors_uncorrected = 1;
 395        }
 396        return def;
 397}
 398
 399/*
 400 * Make certain that we aren't checking a filesystem that is on a
 401 * mounted partition.  Code adapted from e2fsck, Copyright (C) 1993,
 402 * 1994 Theodore Ts'o.  Also licensed under GPL.
 403 */
 404static void check_mount(void)
 405{
 406        if (find_mount_point(device_name, 0)) {
 407                int cont;
 408#if ENABLE_FEATURE_MTAB_SUPPORT
 409                /*
 410                 * If the root is mounted read-only, then /etc/mtab is
 411                 * probably not correct; so we won't issue a warning based on
 412                 * it.
 413                 */
 414                int fd = open(bb_path_mtab_file, O_RDWR);
 415
 416                if (fd < 0 && errno == EROFS)
 417                        return;
 418                close(fd);
 419#endif
 420                printf("%s is mounted. ", device_name);
 421                cont = 0;
 422                if (isatty(0) && isatty(1))
 423                        cont = ask("Do you really want to continue", 0);
 424                if (!cont) {
 425                        puts("Check aborted");
 426                        exit(EXIT_SUCCESS);
 427                }
 428        }
 429}
 430
 431/*
 432 * check_zone_nr checks to see that *nr is a valid zone nr. If it
 433 * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
 434 * if an error was corrected, and returns the zone (0 for no zone
 435 * or a bad zone-number).
 436 */
 437static int check_zone_nr2(uint32_t *nr, smallint *corrected)
 438{
 439        const char *msg;
 440        if (!*nr)
 441                return 0;
 442        if (*nr < FIRSTZONE)
 443                msg = "< FIRSTZONE";
 444        else if (*nr >= ZONES)
 445                msg = ">= ZONES";
 446        else
 447                return *nr;
 448        printf("Zone nr %s in file '%s'. ", msg, current_name);
 449        if (ask("Remove block", 1)) {
 450                *nr = 0;
 451                *corrected = 1;
 452        }
 453        return 0;
 454}
 455
 456static int check_zone_nr(uint16_t *nr, smallint *corrected)
 457{
 458        uint32_t nr32 = *nr;
 459        int r = check_zone_nr2(&nr32, corrected);
 460        *nr = (uint16_t)nr32;
 461        return r;
 462}
 463
 464/*
 465 * read-block reads block nr into the buffer at addr.
 466 */
 467static void read_block(unsigned nr, void *addr)
 468{
 469        if (!nr) {
 470                memset(addr, 0, BLOCK_SIZE);
 471                return;
 472        }
 473        xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET);
 474        if (BLOCK_SIZE != full_read(dev_fd, addr, BLOCK_SIZE)) {
 475                printf("%s: bad block %u in file '%s'\n",
 476                                bb_msg_read_error, nr, current_name);
 477                errors_uncorrected = 1;
 478                memset(addr, 0, BLOCK_SIZE);
 479        }
 480}
 481
 482/*
 483 * write_block writes block nr to disk.
 484 */
 485static void write_block(unsigned nr, void *addr)
 486{
 487        if (!nr)
 488                return;
 489        if (nr < FIRSTZONE || nr >= ZONES) {
 490                puts("Internal error: trying to write bad block\n"
 491                        "Write request ignored");
 492                errors_uncorrected = 1;
 493                return;
 494        }
 495        xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET);
 496        if (BLOCK_SIZE != full_write(dev_fd, addr, BLOCK_SIZE)) {
 497                printf("%s: bad block %u in file '%s'\n",
 498                                bb_msg_write_error, nr, current_name);
 499                errors_uncorrected = 1;
 500        }
 501}
 502
 503/*
 504 * map_block calculates the absolute block nr of a block in a file.
 505 * It sets 'changed' if the inode has needed changing, and re-writes
 506 * any indirect blocks with errors.
 507 */
 508static int map_block(struct minix1_inode *inode, unsigned blknr)
 509{
 510        uint16_t ind[BLOCK_SIZE >> 1];
 511        int block, result;
 512        smallint blk_chg;
 513
 514        if (blknr < 7)
 515                return check_zone_nr(inode->i_zone + blknr, &changed);
 516        blknr -= 7;
 517        if (blknr < 512) {
 518                block = check_zone_nr(inode->i_zone + 7, &changed);
 519                goto common;
 520        }
 521        blknr -= 512;
 522        block = check_zone_nr(inode->i_zone + 8, &changed);
 523        read_block(block, ind); /* double indirect */
 524        blk_chg = 0;
 525        result = check_zone_nr(&ind[blknr / 512], &blk_chg);
 526        if (blk_chg)
 527                write_block(block, ind);
 528        block = result;
 529 common:
 530        read_block(block, ind);
 531        blk_chg = 0;
 532        result = check_zone_nr(&ind[blknr % 512], &blk_chg);
 533        if (blk_chg)
 534                write_block(block, ind);
 535        return result;
 536}
 537
 538#if ENABLE_FEATURE_MINIX2
 539static int map_block2(struct minix2_inode *inode, unsigned blknr)
 540{
 541        uint32_t ind[BLOCK_SIZE >> 2];
 542        int block, result;
 543        smallint blk_chg;
 544
 545        if (blknr < 7)
 546                return check_zone_nr2(inode->i_zone + blknr, &changed);
 547        blknr -= 7;
 548        if (blknr < 256) {
 549                block = check_zone_nr2(inode->i_zone + 7, &changed);
 550                goto common2;
 551        }
 552        blknr -= 256;
 553        if (blknr < 256 * 256) {
 554                block = check_zone_nr2(inode->i_zone + 8, &changed);
 555                goto common1;
 556        }
 557        blknr -= 256 * 256;
 558        block = check_zone_nr2(inode->i_zone + 9, &changed);
 559        read_block(block, ind); /* triple indirect */
 560        blk_chg = 0;
 561        result = check_zone_nr2(&ind[blknr / (256 * 256)], &blk_chg);
 562        if (blk_chg)
 563                write_block(block, ind);
 564        block = result;
 565 common1:
 566        read_block(block, ind); /* double indirect */
 567        blk_chg = 0;
 568        result = check_zone_nr2(&ind[(blknr / 256) % 256], &blk_chg);
 569        if (blk_chg)
 570                write_block(block, ind);
 571        block = result;
 572 common2:
 573        read_block(block, ind);
 574        blk_chg = 0;
 575        result = check_zone_nr2(&ind[blknr % 256], &blk_chg);
 576        if (blk_chg)
 577                write_block(block, ind);
 578        return result;
 579}
 580#endif
 581
 582static void write_superblock(void)
 583{
 584        /*
 585         * Set the state of the filesystem based on whether or not there
 586         * are uncorrected errors.  The filesystem valid flag is
 587         * unconditionally set if we get this far.
 588         */
 589        Super.s_state |= MINIX_VALID_FS | MINIX_ERROR_FS;
 590        if (!errors_uncorrected)
 591                Super.s_state &= ~MINIX_ERROR_FS;
 592
 593        xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
 594        if (BLOCK_SIZE != full_write(dev_fd, superblock_buffer, BLOCK_SIZE))
 595                die("can't write superblock");
 596}
 597
 598static void write_tables(void)
 599{
 600        write_superblock();
 601
 602        if (IMAPS * BLOCK_SIZE != write(dev_fd, inode_map, IMAPS * BLOCK_SIZE))
 603                die("can't write inode map");
 604        if (ZMAPS * BLOCK_SIZE != write(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
 605                die("can't write zone map");
 606        if (INODE_BUFFER_SIZE != write(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
 607                die("can't write inodes");
 608}
 609
 610static void get_dirsize(void)
 611{
 612        int block;
 613        char blk[BLOCK_SIZE];
 614        int size;
 615
 616#if ENABLE_FEATURE_MINIX2
 617        if (version2)
 618                block = Inode2[MINIX_ROOT_INO].i_zone[0];
 619        else
 620#endif
 621                block = Inode1[MINIX_ROOT_INO].i_zone[0];
 622        read_block(block, blk);
 623        for (size = 16; size < BLOCK_SIZE; size <<= 1) {
 624                if (strcmp(blk + size + 2, "..") == 0) {
 625                        dirsize = size;
 626                        namelen = size - 2;
 627                        return;
 628                }
 629        }
 630        /* use defaults */
 631}
 632
 633static void read_superblock(void)
 634{
 635        xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
 636        if (BLOCK_SIZE != full_read(dev_fd, superblock_buffer, BLOCK_SIZE))
 637                die("can't read superblock");
 638        /* already initialized to:
 639        namelen = 14;
 640        dirsize = 16;
 641        version2 = 0;
 642        */
 643        if (MAGIC == MINIX1_SUPER_MAGIC) {
 644        } else if (MAGIC == MINIX1_SUPER_MAGIC2) {
 645                namelen = 30;
 646                dirsize = 32;
 647#if ENABLE_FEATURE_MINIX2
 648        } else if (MAGIC == MINIX2_SUPER_MAGIC) {
 649                version2 = 1;
 650        } else if (MAGIC == MINIX2_SUPER_MAGIC2) {
 651                namelen = 30;
 652                dirsize = 32;
 653                version2 = 1;
 654#endif
 655        } else
 656                die("bad magic number in superblock");
 657        if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
 658                die("only 1k blocks/zones supported");
 659        if (IMAPS * BLOCK_SIZE * 8 < INODES + 1)
 660                die("bad s_imap_blocks field in superblock");
 661        if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1)
 662                die("bad s_zmap_blocks field in superblock");
 663}
 664
 665static void read_tables(void)
 666{
 667        inode_map = xzalloc(IMAPS * BLOCK_SIZE);
 668        zone_map = xzalloc(ZMAPS * BLOCK_SIZE);
 669        inode_buffer = xmalloc(INODE_BUFFER_SIZE);
 670        inode_count = xmalloc(INODES + 1);
 671        zone_count = xmalloc(ZONES);
 672        if (IMAPS * BLOCK_SIZE != read(dev_fd, inode_map, IMAPS * BLOCK_SIZE))
 673                die("can't read inode map");
 674        if (ZMAPS * BLOCK_SIZE != read(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
 675                die("can't read zone map");
 676        if (INODE_BUFFER_SIZE != read(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
 677                die("can't read inodes");
 678        if (NORM_FIRSTZONE != FIRSTZONE) {
 679                puts("warning: firstzone!=norm_firstzone");
 680                errors_uncorrected = 1;
 681        }
 682        get_dirsize();
 683        if (OPT_show) {
 684                printf("%u inodes\n"
 685                        "%u blocks\n"
 686                        "Firstdatazone=%u (%u)\n"
 687                        "Zonesize=%u\n"
 688                        "Maxsize=%u\n"
 689                        "Filesystem state=%u\n"
 690                        "namelen=%u\n\n",
 691                        INODES,
 692                        ZONES,
 693                        FIRSTZONE, NORM_FIRSTZONE,
 694                        BLOCK_SIZE << ZONESIZE,
 695                        MAXSIZE,
 696                        Super.s_state,
 697                        namelen);
 698        }
 699}
 700
 701static void get_inode_common(unsigned nr, uint16_t i_mode)
 702{
 703        total++;
 704        if (!inode_count[nr]) {
 705                if (!inode_in_use(nr)) {
 706                        printf("Inode %u is marked as 'unused', but it is used "
 707                                        "for file '%s'\n", nr, current_name);
 708                        if (OPT_repair) {
 709                                if (ask("Mark as 'in use'", 1))
 710                                        mark_inode(nr);
 711                                else
 712                                        errors_uncorrected = 1;
 713                        }
 714                }
 715                if (S_ISDIR(i_mode))
 716                        directory++;
 717                else if (S_ISREG(i_mode))
 718                        regular++;
 719                else if (S_ISCHR(i_mode))
 720                        chardev++;
 721                else if (S_ISBLK(i_mode))
 722                        blockdev++;
 723                else if (S_ISLNK(i_mode))
 724                        symlinks++;
 725                else if (S_ISSOCK(i_mode));
 726                else if (S_ISFIFO(i_mode));
 727                else {
 728                        printf("%s has mode %05o\n", current_name, i_mode);
 729                }
 730        } else
 731                links++;
 732        if (!++inode_count[nr]) {
 733                puts("Warning: inode count too big");
 734                inode_count[nr]--;
 735                errors_uncorrected = 1;
 736        }
 737}
 738
 739static struct minix1_inode *get_inode(unsigned nr)
 740{
 741        struct minix1_inode *inode;
 742
 743        if (!nr || nr > INODES)
 744                return NULL;
 745        inode = Inode1 + nr;
 746        get_inode_common(nr, inode->i_mode);
 747        return inode;
 748}
 749
 750#if ENABLE_FEATURE_MINIX2
 751static struct minix2_inode *get_inode2(unsigned nr)
 752{
 753        struct minix2_inode *inode;
 754
 755        if (!nr || nr > INODES)
 756                return NULL;
 757        inode = Inode2 + nr;
 758        get_inode_common(nr, inode->i_mode);
 759        return inode;
 760}
 761#endif
 762
 763static void check_root(void)
 764{
 765        struct minix1_inode *inode = Inode1 + MINIX_ROOT_INO;
 766
 767        if (!inode || !S_ISDIR(inode->i_mode))
 768                die("root inode isn't a directory");
 769}
 770
 771#if ENABLE_FEATURE_MINIX2
 772static void check_root2(void)
 773{
 774        struct minix2_inode *inode = Inode2 + MINIX_ROOT_INO;
 775
 776        if (!inode || !S_ISDIR(inode->i_mode))
 777                die("root inode isn't a directory");
 778}
 779#else
 780void check_root2(void);
 781#endif
 782
 783static int add_zone_common(int block, smallint *corrected)
 784{
 785        if (!block)
 786                return 0;
 787        if (zone_count[block]) {
 788                printf("Already used block is reused in file '%s'. ",
 789                                current_name);
 790                if (ask("Clear", 1)) {
 791                        block = 0;
 792                        *corrected = 1;
 793                        return -1; /* "please zero out *znr" */
 794                }
 795        }
 796        if (!zone_in_use(block)) {
 797                printf("Block %d in file '%s' is marked as 'unused'. ",
 798                                block, current_name);
 799                if (ask("Correct", 1))
 800                        mark_zone(block);
 801        }
 802        if (!++zone_count[block])
 803                zone_count[block]--;
 804        return block;
 805}
 806
 807static int add_zone(uint16_t *znr, smallint *corrected)
 808{
 809        int block;
 810
 811        block = check_zone_nr(znr, corrected);
 812        block = add_zone_common(block, corrected);
 813        if (block == -1) {
 814                *znr = 0;
 815                block = 0;
 816        }
 817        return block;
 818}
 819
 820#if ENABLE_FEATURE_MINIX2
 821static int add_zone2(uint32_t *znr, smallint *corrected)
 822{
 823        int block;
 824
 825        block = check_zone_nr2(znr, corrected);
 826        block = add_zone_common(block, corrected);
 827        if (block == -1) {
 828                *znr = 0;
 829                block = 0;
 830        }
 831        return block;
 832}
 833#endif
 834
 835static void add_zone_ind(uint16_t *znr, smallint *corrected)
 836{
 837        int i;
 838        int block;
 839        smallint chg_blk = 0;
 840
 841        block = add_zone(znr, corrected);
 842        if (!block)
 843                return;
 844        read_block(block, add_zone_ind_blk);
 845        for (i = 0; i < (BLOCK_SIZE >> 1); i++)
 846                add_zone(i + (uint16_t *) add_zone_ind_blk, &chg_blk);
 847        if (chg_blk)
 848                write_block(block, add_zone_ind_blk);
 849}
 850
 851#if ENABLE_FEATURE_MINIX2
 852static void add_zone_ind2(uint32_t *znr, smallint *corrected)
 853{
 854        int i;
 855        int block;
 856        smallint chg_blk = 0;
 857
 858        block = add_zone2(znr, corrected);
 859        if (!block)
 860                return;
 861        read_block(block, add_zone_ind_blk);
 862        for (i = 0; i < BLOCK_SIZE >> 2; i++)
 863                add_zone2(i + (uint32_t *) add_zone_ind_blk, &chg_blk);
 864        if (chg_blk)
 865                write_block(block, add_zone_ind_blk);
 866}
 867#endif
 868
 869static void add_zone_dind(uint16_t *znr, smallint *corrected)
 870{
 871        int i;
 872        int block;
 873        smallint chg_blk = 0;
 874
 875        block = add_zone(znr, corrected);
 876        if (!block)
 877                return;
 878        read_block(block, add_zone_dind_blk);
 879        for (i = 0; i < (BLOCK_SIZE >> 1); i++)
 880                add_zone_ind(i + (uint16_t *) add_zone_dind_blk, &chg_blk);
 881        if (chg_blk)
 882                write_block(block, add_zone_dind_blk);
 883}
 884
 885#if ENABLE_FEATURE_MINIX2
 886static void add_zone_dind2(uint32_t *znr, smallint *corrected)
 887{
 888        int i;
 889        int block;
 890        smallint chg_blk = 0;
 891
 892        block = add_zone2(znr, corrected);
 893        if (!block)
 894                return;
 895        read_block(block, add_zone_dind_blk);
 896        for (i = 0; i < BLOCK_SIZE >> 2; i++)
 897                add_zone_ind2(i + (uint32_t *) add_zone_dind_blk, &chg_blk);
 898        if (chg_blk)
 899                write_block(block, add_zone_dind_blk);
 900}
 901
 902static void add_zone_tind2(uint32_t *znr, smallint *corrected)
 903{
 904        int i;
 905        int block;
 906        smallint chg_blk = 0;
 907
 908        block = add_zone2(znr, corrected);
 909        if (!block)
 910                return;
 911        read_block(block, add_zone_tind_blk);
 912        for (i = 0; i < BLOCK_SIZE >> 2; i++)
 913                add_zone_dind2(i + (uint32_t *) add_zone_tind_blk, &chg_blk);
 914        if (chg_blk)
 915                write_block(block, add_zone_tind_blk);
 916}
 917#endif
 918
 919static void check_zones(unsigned i)
 920{
 921        struct minix1_inode *inode;
 922
 923        if (!i || i > INODES)
 924                return;
 925        if (inode_count[i] > 1)         /* have we counted this file already? */
 926                return;
 927        inode = Inode1 + i;
 928        if (!S_ISDIR(inode->i_mode)
 929         && !S_ISREG(inode->i_mode)
 930         && !S_ISLNK(inode->i_mode)
 931        ) {
 932                return;
 933        }
 934        for (i = 0; i < 7; i++)
 935                add_zone(i + inode->i_zone, &changed);
 936        add_zone_ind(7 + inode->i_zone, &changed);
 937        add_zone_dind(8 + inode->i_zone, &changed);
 938}
 939
 940#if ENABLE_FEATURE_MINIX2
 941static void check_zones2(unsigned i)
 942{
 943        struct minix2_inode *inode;
 944
 945        if (!i || i > INODES)
 946                return;
 947        if (inode_count[i] > 1)         /* have we counted this file already? */
 948                return;
 949        inode = Inode2 + i;
 950        if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)
 951                && !S_ISLNK(inode->i_mode))
 952                return;
 953        for (i = 0; i < 7; i++)
 954                add_zone2(i + inode->i_zone, &changed);
 955        add_zone_ind2(7 + inode->i_zone, &changed);
 956        add_zone_dind2(8 + inode->i_zone, &changed);
 957        add_zone_tind2(9 + inode->i_zone, &changed);
 958}
 959#endif
 960
 961static void check_file(struct minix1_inode *dir, unsigned offset)
 962{
 963        struct minix1_inode *inode;
 964        int ino;
 965        char *name;
 966        int block;
 967
 968        block = map_block(dir, offset / BLOCK_SIZE);
 969        read_block(block, check_file_blk);
 970        name = check_file_blk + (offset % BLOCK_SIZE) + 2;
 971        ino = *(uint16_t *) (name - 2);
 972        if (ino > INODES) {
 973                printf("%s contains a bad inode number for file '%.*s'. ",
 974                                current_name, namelen, name);
 975                if (ask("Remove", 1)) {
 976                        *(uint16_t *) (name - 2) = 0;
 977                        write_block(block, check_file_blk);
 978                }
 979                ino = 0;
 980        }
 981        push_filename(name);
 982        inode = get_inode(ino);
 983        pop_filename();
 984        if (!offset) {
 985                if (inode && LONE_CHAR(name, '.'))
 986                        return;
 987                printf("%s: bad directory: '.' isn't first\n", current_name);
 988                errors_uncorrected = 1;
 989        }
 990        if (offset == dirsize) {
 991                if (inode && strcmp("..", name) == 0)
 992                        return;
 993                printf("%s: bad directory: '..' isn't second\n", current_name);
 994                errors_uncorrected = 1;
 995        }
 996        if (!inode)
 997                return;
 998        push_filename(name);
 999        if (OPT_list) {
1000                if (OPT_verbose)
1001                        printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
1002                printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
1003        }
1004        check_zones(ino);
1005        if (inode && S_ISDIR(inode->i_mode))
1006                recursive_check(ino);
1007        pop_filename();
1008}
1009
1010#if ENABLE_FEATURE_MINIX2
1011static void check_file2(struct minix2_inode *dir, unsigned offset)
1012{
1013        struct minix2_inode *inode;
1014        int ino;
1015        char *name;
1016        int block;
1017
1018        block = map_block2(dir, offset / BLOCK_SIZE);
1019        read_block(block, check_file_blk);
1020        name = check_file_blk + (offset % BLOCK_SIZE) + 2;
1021        ino = *(uint16_t *) (name - 2);
1022        if (ino > INODES) {
1023                printf("%s contains a bad inode number for file '%.*s'. ",
1024                                current_name, namelen, name);
1025                if (ask("Remove", 1)) {
1026                        *(uint16_t *) (name - 2) = 0;
1027                        write_block(block, check_file_blk);
1028                }
1029                ino = 0;
1030        }
1031        push_filename(name);
1032        inode = get_inode2(ino);
1033        pop_filename();
1034        if (!offset) {
1035                if (inode && LONE_CHAR(name, '.'))
1036                        return;
1037                printf("%s: bad directory: '.' isn't first\n", current_name);
1038                errors_uncorrected = 1;
1039        }
1040        if (offset == dirsize) {
1041                if (inode && strcmp("..", name) == 0)
1042                        return;
1043                printf("%s: bad directory: '..' isn't second\n", current_name);
1044                errors_uncorrected = 1;
1045        }
1046        if (!inode)
1047                return;
1048        push_filename(name);
1049        if (OPT_list) {
1050                if (OPT_verbose)
1051                        printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
1052                printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
1053        }
1054        check_zones2(ino);
1055        if (inode && S_ISDIR(inode->i_mode))
1056                recursive_check2(ino);
1057        pop_filename();
1058}
1059#endif
1060
1061static void recursive_check(unsigned ino)
1062{
1063        struct minix1_inode *dir;
1064        unsigned offset;
1065
1066        dir = Inode1 + ino;
1067        if (!S_ISDIR(dir->i_mode))
1068                die("internal error");
1069        if (dir->i_size < 2 * dirsize) {
1070                printf("%s: bad directory: size<32", current_name);
1071                errors_uncorrected = 1;
1072        }
1073        for (offset = 0; offset < dir->i_size; offset += dirsize)
1074                check_file(dir, offset);
1075}
1076
1077#if ENABLE_FEATURE_MINIX2
1078static void recursive_check2(unsigned ino)
1079{
1080        struct minix2_inode *dir;
1081        unsigned offset;
1082
1083        dir = Inode2 + ino;
1084        if (!S_ISDIR(dir->i_mode))
1085                die("internal error");
1086        if (dir->i_size < 2 * dirsize) {
1087                printf("%s: bad directory: size<32", current_name);
1088                errors_uncorrected = 1;
1089        }
1090        for (offset = 0; offset < dir->i_size; offset += dirsize)
1091                check_file2(dir, offset);
1092}
1093#endif
1094
1095static int bad_zone(int i)
1096{
1097        char buffer[BLOCK_SIZE];
1098
1099        xlseek(dev_fd, BLOCK_SIZE * i, SEEK_SET);
1100        return (BLOCK_SIZE != full_read(dev_fd, buffer, BLOCK_SIZE));
1101}
1102
1103static void check_counts(void)
1104{
1105        int i;
1106
1107        for (i = 1; i <= INODES; i++) {
1108                if (OPT_warn_mode && Inode1[i].i_mode && !inode_in_use(i)) {
1109                        printf("Inode %d has non-zero mode. ", i);
1110                        if (ask("Clear", 1)) {
1111                                Inode1[i].i_mode = 0;
1112                                changed = 1;
1113                        }
1114                }
1115                if (!inode_count[i]) {
1116                        if (!inode_in_use(i))
1117                                continue;
1118                        printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
1119                        if (ask("Clear", 1))
1120                                unmark_inode(i);
1121                        continue;
1122                }
1123                if (!inode_in_use(i)) {
1124                        printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
1125                        if (ask("Set", 1))
1126                                mark_inode(i);
1127                }
1128                if (Inode1[i].i_nlinks != inode_count[i]) {
1129                        printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
1130                                i, Inode1[i].i_mode, Inode1[i].i_nlinks,
1131                                inode_count[i]);
1132                        if (ask("Set i_nlinks to count", 1)) {
1133                                Inode1[i].i_nlinks = inode_count[i];
1134                                changed = 1;
1135                        }
1136                }
1137        }
1138        for (i = FIRSTZONE; i < ZONES; i++) {
1139                if ((zone_in_use(i) != 0) == zone_count[i])
1140                        continue;
1141                if (!zone_count[i]) {
1142                        if (bad_zone(i))
1143                                continue;
1144                        printf("Zone %d is marked 'in use', but no file uses it. ", i);
1145                        if (ask("Unmark", 1))
1146                                unmark_zone(i);
1147                        continue;
1148                }
1149                printf("Zone %d: %sin use, counted=%d\n",
1150                        i, zone_in_use(i) ? "" : "not ", zone_count[i]);
1151        }
1152}
1153
1154#if ENABLE_FEATURE_MINIX2
1155static void check_counts2(void)
1156{
1157        int i;
1158
1159        for (i = 1; i <= INODES; i++) {
1160                if (OPT_warn_mode && Inode2[i].i_mode && !inode_in_use(i)) {
1161                        printf("Inode %d has non-zero mode. ", i);
1162                        if (ask("Clear", 1)) {
1163                                Inode2[i].i_mode = 0;
1164                                changed = 1;
1165                        }
1166                }
1167                if (!inode_count[i]) {
1168                        if (!inode_in_use(i))
1169                                continue;
1170                        printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
1171                        if (ask("Clear", 1))
1172                                unmark_inode(i);
1173                        continue;
1174                }
1175                if (!inode_in_use(i)) {
1176                        printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
1177                        if (ask("Set", 1))
1178                                mark_inode(i);
1179                }
1180                if (Inode2[i].i_nlinks != inode_count[i]) {
1181                        printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
1182                                i, Inode2[i].i_mode, Inode2[i].i_nlinks,
1183                                inode_count[i]);
1184                        if (ask("Set i_nlinks to count", 1)) {
1185                                Inode2[i].i_nlinks = inode_count[i];
1186                                changed = 1;
1187                        }
1188                }
1189        }
1190        for (i = FIRSTZONE; i < ZONES; i++) {
1191                if ((zone_in_use(i) != 0) == zone_count[i])
1192                        continue;
1193                if (!zone_count[i]) {
1194                        if (bad_zone(i))
1195                                continue;
1196                        printf("Zone %d is marked 'in use', but no file uses it. ", i);
1197                        if (ask("Unmark", 1))
1198                                unmark_zone(i);
1199                        continue;
1200                }
1201                printf("Zone %d: %sin use, counted=%d\n",
1202                        i, zone_in_use(i) ? "" : "not ", zone_count[i]);
1203        }
1204}
1205#endif
1206
1207static void check(void)
1208{
1209        memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
1210        memset(zone_count, 0, ZONES * sizeof(*zone_count));
1211        check_zones(MINIX_ROOT_INO);
1212        recursive_check(MINIX_ROOT_INO);
1213        check_counts();
1214}
1215
1216#if ENABLE_FEATURE_MINIX2
1217static void check2(void)
1218{
1219        memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
1220        memset(zone_count, 0, ZONES * sizeof(*zone_count));
1221        check_zones2(MINIX_ROOT_INO);
1222        recursive_check2(MINIX_ROOT_INO);
1223        check_counts2();
1224}
1225#else
1226void check2(void);
1227#endif
1228
1229int fsck_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1230int fsck_minix_main(int argc UNUSED_PARAM, char **argv)
1231{
1232        int retcode = 0;
1233
1234        xfunc_error_retval = 8;
1235
1236        INIT_G();
1237
1238        getopt32(argv, "^" OPTION_STR "\0" "=1:ar" /* one arg; -a assumes -r */);
1239        argv += optind;
1240        device_name = argv[0];
1241
1242        check_mount();  /* trying to check a mounted filesystem? */
1243        if (OPT_manual) {
1244                if (!isatty(0) || !isatty(1))
1245                        die("need terminal for interactive repairs");
1246        }
1247        xmove_fd(xopen(device_name, OPT_repair ? O_RDWR : O_RDONLY), dev_fd);
1248
1249        /*sync(); paranoia? */
1250        read_superblock();
1251
1252        /*
1253         * Determine whether or not we should continue with the checking.
1254         * This is based on the status of the filesystem valid and error
1255         * flags and whether or not the -f switch was specified on the
1256         * command line.
1257         */
1258        printf("%s: %s\n", applet_name, bb_banner);
1259
1260        if (!(Super.s_state & MINIX_ERROR_FS)
1261         && (Super.s_state & MINIX_VALID_FS) && !OPT_force
1262        ) {
1263                if (OPT_repair)
1264                        printf("%s is clean, check is skipped\n", device_name);
1265                return 0;
1266        } else if (OPT_force)
1267                printf("Forcing filesystem check on %s\n", device_name);
1268        else if (OPT_repair)
1269                printf("Filesystem on %s is dirty, needs checking\n",
1270                        device_name);
1271
1272        read_tables();
1273
1274        if (OPT_manual) {
1275                set_termios_to_raw(STDIN_FILENO, &sv_termios, 0);
1276                termios_set = 1;
1277        }
1278
1279        if (version2) {
1280                check_root2();
1281                check2();
1282        } else {
1283                check_root();
1284                check();
1285        }
1286
1287        if (OPT_verbose) {
1288                int i, free_cnt;
1289
1290                for (i = 1, free_cnt = 0; i <= INODES; i++)
1291                        if (!inode_in_use(i))
1292                                free_cnt++;
1293                printf("\n%6u inodes used (%u%%)\n", (INODES - free_cnt),
1294                        100 * (INODES - free_cnt) / INODES);
1295                for (i = FIRSTZONE, free_cnt = 0; i < ZONES; i++)
1296                        if (!zone_in_use(i))
1297                                free_cnt++;
1298                printf("%6u zones used (%u%%)\n\n"
1299                        "%6u regular files\n"
1300                        "%6u directories\n"
1301                        "%6u character device files\n"
1302                        "%6u block device files\n"
1303                        "%6u links\n"
1304                        "%6u symbolic links\n"
1305                        "------\n"
1306                        "%6u files\n",
1307                        (ZONES - free_cnt), 100 * (ZONES - free_cnt) / ZONES,
1308                        regular, directory, chardev, blockdev,
1309                        links - 2 * directory + 1, symlinks,
1310                        total - 2 * directory + 1);
1311        }
1312        if (changed) {
1313                write_tables();
1314                puts("FILE SYSTEM HAS BEEN CHANGED");
1315                sync();
1316        } else if (OPT_repair)
1317                write_superblock();
1318
1319        if (OPT_manual)
1320                tcsetattr_stdin_TCSANOW(&sv_termios);
1321
1322        if (changed)
1323                retcode += 3;
1324        if (errors_uncorrected)
1325                retcode += 4;
1326        return retcode;
1327}
1328