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