|  | /* | 
|  | * fsck.minix.c - a file system consistency checker for Linux. | 
|  | * | 
|  | * (C) 1991, 1992 Linus Torvalds. This file may be redistributed | 
|  | * as per the GNU copyleft. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * 09.11.91  -  made the first rudimetary functions | 
|  | * | 
|  | * 10.11.91  -  updated, does checking, no repairs yet. | 
|  | *		Sent out to the mailing-list for testing. | 
|  | * | 
|  | * 14.11.91  -	Testing seems to have gone well. Added some | 
|  | *		correction-code, and changed some functions. | 
|  | * | 
|  | * 15.11.91  -  More correction code. Hopefully it notices most | 
|  | *		cases now, and tries to do something about them. | 
|  | * | 
|  | * 16.11.91  -  More corrections (thanks to Mika Jalava). Most | 
|  | *		things seem to work now. Yeah, sure. | 
|  | * | 
|  | * | 
|  | * 19.04.92  -	Had to start over again from this old version, as a | 
|  | *		kernel bug ate my enhanced fsck in february. | 
|  | * | 
|  | * 28.02.93  -	added support for different directory entry sizes.. | 
|  | * | 
|  | * Sat Mar  6 18:59:42 1993, faith@cs.unc.edu: Output namelen with | 
|  | *                           super-block information | 
|  | * | 
|  | * Sat Oct  9 11:17:11 1993, faith@cs.unc.edu: make exit status conform | 
|  | *                           to that required by fsutil | 
|  | * | 
|  | * Mon Jan  3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu) | 
|  | *			      Added support for file system valid flag.  Also | 
|  | *			      added program_version variable and output of | 
|  | *			      program name and version number when program | 
|  | *			      is executed. | 
|  | * | 
|  | * 30.10.94 - added support for v2 filesystem | 
|  | *            (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de) | 
|  | * | 
|  | * 10.12.94  -  added test to prevent checking of mounted fs adapted | 
|  | *              from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck | 
|  | *              program.  (Daniel Quinlan, quinlan@yggdrasil.com) | 
|  | * | 
|  | * 01.07.96  - Fixed the v2 fs stuff to use the right #defines and such | 
|  | *	       for modern libcs (janl@math.uio.no, Nicolai Langfeldt) | 
|  | * | 
|  | * 02.07.96  - Added C bit fiddling routines from rmk@ecs.soton.ac.uk | 
|  | *             (Russell King).  He made them for ARM.  It would seem | 
|  | *	       that the ARM is powerful enough to do this in C whereas | 
|  | *             i386 and m64k must use assembly to get it fast >:-) | 
|  | *	       This should make minix fsck systemindependent. | 
|  | *	       (janl@math.uio.no, Nicolai Langfeldt) | 
|  | * | 
|  | * 04.11.96  - Added minor fixes from Andreas Schwab to avoid compiler | 
|  | *             warnings.  Added mc68k bitops from | 
|  | *	       Joerg Dorchain <dorchain@mpi-sb.mpg.de>. | 
|  | * | 
|  | * 06.11.96  - Added v2 code submitted by Joerg Dorchain, but written by | 
|  | *             Andreas Schwab. | 
|  | * | 
|  | * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL> | 
|  | * - added Native Language Support | 
|  | * | 
|  | * 2008-04-06 James Youngman <jay@gnu.org> | 
|  | * - Issue better error message if we fail to open the device. | 
|  | * - Restore terminal state if we get a fatal signal. | 
|  | * | 
|  | * | 
|  | * I've had no time to add comments - hopefully the function names | 
|  | * are comments enough. As with all file system checkers, this assumes | 
|  | * the file system is quiescent - don't use it on a mounted device | 
|  | * unless you can be sure nobody is writing to it (and remember that the | 
|  | * kernel can write to it when it searches for files). | 
|  | * | 
|  | * Usuage: fsck [-larvsm] device | 
|  | *	-l for a listing of all the filenames | 
|  | *	-a for automatic repairs (not implemented) | 
|  | *	-r for repairs (interactive) (not implemented) | 
|  | *	-v for verbose (tells how many files) | 
|  | *	-s for super-block info | 
|  | *	-m for minix-like "mode not cleared" warnings | 
|  | *	-f force filesystem check even if filesystem marked as valid | 
|  | * | 
|  | * The device may be a block device or a image of one, but this isn't | 
|  | * enforced (but it's not much fun on a character device :-). | 
|  | */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdarg.h> | 
|  | #include <errno.h> | 
|  | #include <unistd.h> | 
|  | #include <string.h> | 
|  | #include <fcntl.h> | 
|  | #include <ctype.h> | 
|  | #include <stdlib.h> | 
|  | #include <termios.h> | 
|  | #include <mntent.h> | 
|  | #include <sys/stat.h> | 
|  | #include <signal.h> | 
|  |  | 
|  | #include "c.h" | 
|  | #include "exitcodes.h" | 
|  | #include "minix_programs.h" | 
|  | #include "nls.h" | 
|  | #include "pathnames.h" | 
|  | #include "bitops.h" | 
|  | #include "ismounted.h" | 
|  | #include "all-io.h" | 
|  | #include "closestream.h" | 
|  | #include "rpmatch.h" | 
|  | #include "strutils.h" | 
|  |  | 
|  | #define ROOT_INO 1 | 
|  | #define YESNO_LENGTH 64 | 
|  |  | 
|  | /* Global variables used in minix_programs.h inline fuctions */ | 
|  | int fs_version = 1; | 
|  | char *super_block_buffer; | 
|  |  | 
|  | static char *inode_buffer; | 
|  |  | 
|  | #define Inode (((struct minix_inode *) inode_buffer) - 1) | 
|  | #define Inode2 (((struct minix2_inode *) inode_buffer) - 1) | 
|  |  | 
|  | static char *device_name; | 
|  | static int IN; | 
|  | static int repair, automatic, verbose, list, show, warn_mode, force; | 
|  | static int directory, regular, blockdev, chardev, links, symlinks, total; | 
|  |  | 
|  | static int changed;		/* flags if the filesystem has been changed */ | 
|  | static int errors_uncorrected;	/* flag if some error was not corrected */ | 
|  | static size_t dirsize = 16; | 
|  | static size_t namelen = 14; | 
|  | static struct termios termios; | 
|  | static volatile sig_atomic_t termios_set; | 
|  |  | 
|  | /* File-name data */ | 
|  | #define MAX_DEPTH 50 | 
|  | static int name_depth; | 
|  | static char name_list[MAX_DEPTH][MINIX_NAME_MAX + 1]; | 
|  |  | 
|  | /* Copy of the previous, just for error reporting - see get_current_name.  This | 
|  | * is a waste of 12kB or so.  */ | 
|  | static char current_name[MAX_DEPTH * (MINIX_NAME_MAX + 1) + 1]; | 
|  |  | 
|  | #define MAGIC (Super.s_magic) | 
|  |  | 
|  | static unsigned char *inode_count = NULL; | 
|  | static unsigned char *zone_count = NULL; | 
|  |  | 
|  | static void recursive_check(unsigned int ino); | 
|  | static void recursive_check2(unsigned int ino); | 
|  |  | 
|  | static char *inode_map; | 
|  | static char *zone_map; | 
|  |  | 
|  | #define inode_in_use(x) (isset(inode_map,(x)) != 0) | 
|  | #define zone_in_use(x) (isset(zone_map,(x)-get_first_zone()+1) != 0) | 
|  |  | 
|  | #define mark_inode(x) (setbit(inode_map,(x)),changed=1) | 
|  | #define unmark_inode(x) (clrbit(inode_map,(x)),changed=1) | 
|  |  | 
|  | #define mark_zone(x) (setbit(zone_map,(x)-get_first_zone()+1),changed=1) | 
|  | #define unmark_zone(x) (clrbit(zone_map,(x)-get_first_zone()+1),changed=1) | 
|  |  | 
|  | static void | 
|  | reset(void) { | 
|  | if (termios_set) | 
|  | tcsetattr(STDIN_FILENO, TCSANOW, &termios); | 
|  | } | 
|  |  | 
|  | static void | 
|  | fatalsig(int sig) { | 
|  | /* We received a fatal signal.  Reset the terminal.  Also reset the | 
|  | * signal handler and re-send the signal, so that the parent process | 
|  | * knows which signal actually caused our death.  */ | 
|  | signal(sig, SIG_DFL); | 
|  | reset(); | 
|  | raise(sig); | 
|  | } | 
|  |  | 
|  | static void | 
|  | leave(int status) { | 
|  | reset(); | 
|  | exit(status); | 
|  | } | 
|  |  | 
|  | static void | 
|  | usage(void) { | 
|  | fputs(USAGE_HEADER, stderr); | 
|  | fprintf(stderr, | 
|  | _(" %s [options] <device>\n"), program_invocation_short_name); | 
|  |  | 
|  | fputs(USAGE_SEPARATOR, stderr); | 
|  | fputs(_("Check the consistency of a Minix filesystem.\n"), stderr); | 
|  |  | 
|  | fputs(USAGE_OPTIONS, stderr); | 
|  | fputs(_(" -l  list all filenames\n"), stderr); | 
|  | fputs(_(" -a  automatic repair\n"), stderr); | 
|  | fputs(_(" -r  interactive repair\n"), stderr); | 
|  | fputs(_(" -v  be verbose\n"), stderr); | 
|  | fputs(_(" -s  output super-block information\n"), stderr); | 
|  | fputs(_(" -m  activate mode not cleared warnings\n"), stderr); | 
|  | fputs(_(" -f  force check\n"), stderr); | 
|  | fputs(USAGE_SEPARATOR, stderr); | 
|  | fputs(USAGE_VERSION, stderr); | 
|  | fprintf(stderr, USAGE_MAN_TAIL("fsck.minix(8)")); | 
|  | leave(FSCK_EX_USAGE); | 
|  | } | 
|  |  | 
|  | static void die(const char *fmt, ...) | 
|  | __attribute__ ((__format__(__printf__, 1, 2))); | 
|  |  | 
|  | static void | 
|  | die(const char *fmt, ...) { | 
|  | va_list ap; | 
|  |  | 
|  | fprintf(stderr, UTIL_LINUX_VERSION); | 
|  | va_start(ap, fmt); | 
|  | vfprintf(stderr, fmt, ap); | 
|  | va_end(ap); | 
|  | fputc('\n', stderr); | 
|  | leave(FSCK_EX_ERROR); | 
|  | } | 
|  |  | 
|  | /* This simply goes through the file-name data and prints out the current file.  */ | 
|  | static void | 
|  | get_current_name(void) { | 
|  | int i = 0, ct; | 
|  | char *p, *q; | 
|  |  | 
|  | q = current_name; | 
|  | while (i < name_depth) { | 
|  | p = name_list[i++]; | 
|  | ct = namelen; | 
|  | *q++ = '/'; | 
|  | while (ct-- && *p) | 
|  | *q++ = *p++; | 
|  | } | 
|  | if (i == 0) | 
|  | *q++ = '/'; | 
|  | *q = 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ask(const char *string, int def) { | 
|  | int resp; | 
|  | char input[YESNO_LENGTH]; | 
|  |  | 
|  | if (!repair) { | 
|  | printf("\n"); | 
|  | errors_uncorrected = 1; | 
|  | return 0; | 
|  | } | 
|  | if (automatic) { | 
|  | printf("\n"); | 
|  | if (!def) | 
|  | errors_uncorrected = 1; | 
|  | return def; | 
|  | } | 
|  | /* TRANSLATORS: these yes no questions uses rpmatch(), and should be | 
|  | * translated.  */ | 
|  | printf(def ? _("%s (y/n)? ") : _("%s (n/y)? "), string); | 
|  | fflush(stdout); | 
|  | ignore_result( fgets(input, YESNO_LENGTH, stdin) ); | 
|  | resp = rpmatch(input); | 
|  | switch (resp) { | 
|  | case RPMATCH_INVALID: | 
|  | /* def = def */ | 
|  | break; | 
|  | case RPMATCH_NO: | 
|  | case RPMATCH_YES: | 
|  | def = resp; | 
|  | break; | 
|  | default: | 
|  | /* rpmatch bug? */ | 
|  | abort(); | 
|  | } | 
|  | if (def) | 
|  | printf(_("y\n")); | 
|  | else { | 
|  | printf(_("n\n")); | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | return def; | 
|  | } | 
|  |  | 
|  | /* Make certain that we aren't checking a filesystem that is on a mounted | 
|  | * partition.  Code adapted from e2fsck, Copyright (C) 1993, 1994 Theodore | 
|  | * Ts'o.  Also licensed under GPL.  */ | 
|  | static void | 
|  | check_mount(void) { | 
|  | int cont; | 
|  |  | 
|  | if (!is_mounted(device_name)) | 
|  | return; | 
|  |  | 
|  | printf(_("%s is mounted.	 "), device_name); | 
|  | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) | 
|  | cont = ask(_("Do you really want to continue"), 0); | 
|  | else | 
|  | cont = 0; | 
|  | if (!cont) { | 
|  | printf(_("check aborted.\n")); | 
|  | exit(FSCK_EX_OK); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* check_zone_nr checks to see that *nr is a valid zone nr.  If it isn't, it | 
|  | * will possibly be repaired.  Check_zone_nr sets *corrected if an error was | 
|  | * corrected, and returns the zone (0 for no zone or a bad zone-number).  */ | 
|  | static int | 
|  | check_zone_nr(unsigned short *nr, int *corrected) { | 
|  | if (!*nr) | 
|  | return 0; | 
|  |  | 
|  | if (*nr < get_first_zone()) { | 
|  | get_current_name(); | 
|  | printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name); | 
|  | } else if (*nr >= get_nzones()) { | 
|  | get_current_name(); | 
|  | printf(_("Zone nr >= ZONES in file `%s'."), current_name); | 
|  | } else | 
|  | return *nr; | 
|  |  | 
|  | if (ask(_("Remove block"), 1)) { | 
|  | *nr = 0; | 
|  | *corrected = 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | check_zone_nr2(unsigned int *nr, int *corrected) { | 
|  | if (!*nr) | 
|  | return 0; | 
|  |  | 
|  | if (*nr < get_first_zone()) { | 
|  | get_current_name(); | 
|  | printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name); | 
|  | } else if (*nr >= get_nzones()) { | 
|  | get_current_name(); | 
|  | printf(_("Zone nr >= ZONES in file `%s'."), current_name); | 
|  | } else | 
|  | return *nr; | 
|  |  | 
|  | if (ask(_("Remove block"), 1)) { | 
|  | *nr = 0; | 
|  | *corrected = 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* read-block reads block nr into the buffer at addr.  */ | 
|  | static void | 
|  | read_block(unsigned int nr, char *addr) { | 
|  | if (!nr) { | 
|  | memset(addr, 0, MINIX_BLOCK_SIZE); | 
|  | return; | 
|  | } | 
|  | if (MINIX_BLOCK_SIZE * nr != lseek(IN, MINIX_BLOCK_SIZE * nr, SEEK_SET)) { | 
|  | get_current_name(); | 
|  | printf(_("Read error: unable to seek to block in file '%s'\n"), | 
|  | current_name); | 
|  | memset(addr, 0, MINIX_BLOCK_SIZE); | 
|  | errors_uncorrected = 1; | 
|  | } else if (MINIX_BLOCK_SIZE != read(IN, addr, MINIX_BLOCK_SIZE)) { | 
|  | get_current_name(); | 
|  | printf(_("Read error: bad block in file '%s'\n"), current_name); | 
|  | memset(addr, 0, MINIX_BLOCK_SIZE); | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* write_block writes block nr to disk.  */ | 
|  | static void | 
|  | write_block(unsigned int nr, char *addr) { | 
|  | if (!nr) | 
|  | return; | 
|  | if (nr < get_first_zone() || nr >= get_nzones()) { | 
|  | printf(_("Internal error: trying to write bad block\n" | 
|  | "Write request ignored\n")); | 
|  | errors_uncorrected = 1; | 
|  | return; | 
|  | } | 
|  | if (MINIX_BLOCK_SIZE * nr != lseek(IN, MINIX_BLOCK_SIZE * nr, SEEK_SET)) | 
|  | die(_("seek failed in write_block")); | 
|  | if (MINIX_BLOCK_SIZE != write(IN, addr, MINIX_BLOCK_SIZE)) { | 
|  | get_current_name(); | 
|  | printf(_("Write error: bad block in file '%s'\n"), | 
|  | current_name); | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* map-block calculates the absolute block nr of a block in a file.  It sets | 
|  | * 'changed' if the inode has needed changing, and re-writes any indirect | 
|  | * blocks with errors.  */ | 
|  | static int | 
|  | map_block(struct minix_inode *inode, unsigned int blknr) { | 
|  | unsigned short ind[MINIX_BLOCK_SIZE >> 1]; | 
|  | unsigned short dind[MINIX_BLOCK_SIZE >> 1]; | 
|  | int blk_chg, block, result; | 
|  |  | 
|  | if (blknr < 7) | 
|  | return check_zone_nr(inode->i_zone + blknr, &changed); | 
|  | blknr -= 7; | 
|  | if (blknr < 512) { | 
|  | block = check_zone_nr(inode->i_zone + 7, &changed); | 
|  | read_block(block, (char *)ind); | 
|  | blk_chg = 0; | 
|  | result = check_zone_nr(blknr + ind, &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, (char *)ind); | 
|  | return result; | 
|  | } | 
|  | blknr -= 512; | 
|  | block = check_zone_nr(inode->i_zone + 8, &changed); | 
|  | read_block(block, (char *)dind); | 
|  | blk_chg = 0; | 
|  | result = check_zone_nr(dind + (blknr / 512), &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, (char *)dind); | 
|  | block = result; | 
|  | read_block(block, (char *)ind); | 
|  | blk_chg = 0; | 
|  | result = check_zone_nr(ind + (blknr % 512), &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, (char *)ind); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static int | 
|  | map_block2(struct minix2_inode *inode, unsigned int blknr) { | 
|  | unsigned int ind[MINIX_BLOCK_SIZE >> 2]; | 
|  | unsigned int dind[MINIX_BLOCK_SIZE >> 2]; | 
|  | unsigned int tind[MINIX_BLOCK_SIZE >> 2]; | 
|  | int blk_chg, block, result; | 
|  |  | 
|  | if (blknr < 7) | 
|  | return check_zone_nr2(inode->i_zone + blknr, &changed); | 
|  | blknr -= 7; | 
|  | if (blknr < 256) { | 
|  | block = check_zone_nr2(inode->i_zone + 7, &changed); | 
|  | read_block(block, (char *)ind); | 
|  | blk_chg = 0; | 
|  | result = check_zone_nr2(blknr + ind, &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, (char *)ind); | 
|  | return result; | 
|  | } | 
|  | blknr -= 256; | 
|  | if (blknr < 256 * 256) { | 
|  | block = check_zone_nr2(inode->i_zone + 8, &changed); | 
|  | read_block(block, (char *)dind); | 
|  | blk_chg = 0; | 
|  | result = check_zone_nr2(dind + blknr / 256, &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, (char *)dind); | 
|  | block = result; | 
|  | read_block(block, (char *)ind); | 
|  | blk_chg = 0; | 
|  | result = check_zone_nr2(ind + blknr % 256, &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, (char *)ind); | 
|  | return result; | 
|  | } | 
|  | blknr -= 256 * 256; | 
|  | block = check_zone_nr2(inode->i_zone + 9, &changed); | 
|  | read_block(block, (char *)tind); | 
|  | blk_chg = 0; | 
|  | result = check_zone_nr2(tind + blknr / (256 * 256), &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, (char *)tind); | 
|  | block = result; | 
|  | read_block(block, (char *)dind); | 
|  | blk_chg = 0; | 
|  | result = check_zone_nr2(dind + (blknr / 256) % 256, &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, (char *)dind); | 
|  | block = result; | 
|  | read_block(block, (char *)ind); | 
|  | blk_chg = 0; | 
|  | result = check_zone_nr2(ind + blknr % 256, &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, (char *)ind); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static void | 
|  | write_super_block(void) { | 
|  | /* Set the state of the filesystem based on whether or not there are | 
|  | * uncorrected errors.  The filesystem valid flag is unconditionally | 
|  | * set if we get this far.  */ | 
|  | Super.s_state |= MINIX_VALID_FS; | 
|  | if (errors_uncorrected) | 
|  | Super.s_state |= MINIX_ERROR_FS; | 
|  | else | 
|  | Super.s_state &= ~MINIX_ERROR_FS; | 
|  |  | 
|  | if (MINIX_BLOCK_SIZE != lseek(IN, MINIX_BLOCK_SIZE, SEEK_SET)) | 
|  | die(_("seek failed in write_super_block")); | 
|  | if (MINIX_BLOCK_SIZE != write(IN, super_block_buffer, MINIX_BLOCK_SIZE)) | 
|  | die(_("unable to write super-block")); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void | 
|  | write_tables(void) { | 
|  | unsigned long buffsz = get_inode_buffer_size(); | 
|  | unsigned long imaps = get_nimaps(); | 
|  | unsigned long zmaps = get_nzmaps(); | 
|  |  | 
|  | write_super_block(); | 
|  |  | 
|  | if (write_all(IN, inode_map, imaps * MINIX_BLOCK_SIZE)) | 
|  | die(_("Unable to write inode map")); | 
|  |  | 
|  | if (write_all(IN, zone_map, zmaps * MINIX_BLOCK_SIZE)) | 
|  | die(_("Unable to write zone map")); | 
|  |  | 
|  | if (write_all(IN, inode_buffer, buffsz)) | 
|  | die(_("Unable to write inodes")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | get_dirsize(void) { | 
|  | int block; | 
|  | char blk[MINIX_BLOCK_SIZE]; | 
|  | size_t size; | 
|  |  | 
|  | if (fs_version == 2) | 
|  | block = Inode2[ROOT_INO].i_zone[0]; | 
|  | else | 
|  | block = Inode[ROOT_INO].i_zone[0]; | 
|  | read_block(block, blk); | 
|  |  | 
|  | for (size = 16; size < MINIX_BLOCK_SIZE; size <<= 1) { | 
|  | if (strcmp(blk + size + 2, "..") == 0) { | 
|  | dirsize = size; | 
|  | namelen = size - 2; | 
|  | return; | 
|  | } | 
|  | } | 
|  | /* use defaults */ | 
|  | } | 
|  |  | 
|  | static void | 
|  | read_superblock(void) { | 
|  | if (MINIX_BLOCK_SIZE != lseek(IN, MINIX_BLOCK_SIZE, SEEK_SET)) | 
|  | die(_("seek failed")); | 
|  |  | 
|  | super_block_buffer = calloc(1, MINIX_BLOCK_SIZE); | 
|  | if (!super_block_buffer) | 
|  | die(_("unable to alloc buffer for superblock")); | 
|  |  | 
|  | if (MINIX_BLOCK_SIZE != read(IN, super_block_buffer, MINIX_BLOCK_SIZE)) | 
|  | die(_("unable to read super block")); | 
|  | if (MAGIC == MINIX_SUPER_MAGIC) { | 
|  | namelen = 14; | 
|  | dirsize = 16; | 
|  | fs_version = 1; | 
|  | } else if (MAGIC == MINIX_SUPER_MAGIC2) { | 
|  | namelen = 30; | 
|  | dirsize = 32; | 
|  | fs_version = 1; | 
|  | } else if (MAGIC == MINIX2_SUPER_MAGIC) { | 
|  | namelen = 14; | 
|  | dirsize = 16; | 
|  | fs_version = 2; | 
|  | } else if (MAGIC == MINIX2_SUPER_MAGIC2) { | 
|  | namelen = 30; | 
|  | dirsize = 32; | 
|  | fs_version = 2; | 
|  | } else | 
|  | die(_("bad magic number in super-block")); | 
|  | if (get_zone_size() != 0 || MINIX_BLOCK_SIZE != 1024) | 
|  | die(_("Only 1k blocks/zones supported")); | 
|  | if (get_nimaps() * MINIX_BLOCK_SIZE * 8 < get_ninodes() + 1) | 
|  | die(_("bad s_imap_blocks field in super-block")); | 
|  | if (get_nzmaps() * MINIX_BLOCK_SIZE * 8 < | 
|  | get_nzones() - get_first_zone() + 1) | 
|  | die(_("bad s_zmap_blocks field in super-block")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | read_tables(void) { | 
|  | unsigned long inodes = get_ninodes(); | 
|  | size_t buffsz = get_inode_buffer_size(); | 
|  | off_t norm_first_zone = first_zone_data(); | 
|  | off_t first_zone = get_first_zone(); | 
|  | unsigned long zones = get_nzones(); | 
|  | unsigned long imaps = get_nimaps(); | 
|  | unsigned long zmaps = get_nzmaps(); | 
|  | ssize_t rc; | 
|  |  | 
|  | inode_map = malloc(imaps * MINIX_BLOCK_SIZE); | 
|  | if (!inode_map) | 
|  | die(_("Unable to allocate buffer for inode map")); | 
|  | zone_map = malloc(zmaps * MINIX_BLOCK_SIZE); | 
|  | if (!zone_map) | 
|  | die(_("Unable to allocate buffer for zone map")); | 
|  | inode_buffer = malloc(buffsz); | 
|  | if (!inode_buffer) | 
|  | die(_("Unable to allocate buffer for inodes")); | 
|  | inode_count = calloc(1, inodes + 1); | 
|  | if (!inode_count) | 
|  | die(_("Unable to allocate buffer for inode count")); | 
|  | zone_count = calloc(1, zones); | 
|  | if (!zone_count) | 
|  | die(_("Unable to allocate buffer for zone count")); | 
|  |  | 
|  | rc = read(IN, inode_map, imaps * MINIX_BLOCK_SIZE); | 
|  | if (rc < 0 || imaps * MINIX_BLOCK_SIZE != (size_t) rc) | 
|  | die(_("Unable to read inode map")); | 
|  |  | 
|  | rc = read(IN, zone_map, zmaps * MINIX_BLOCK_SIZE); | 
|  | if (rc < 0 || zmaps * MINIX_BLOCK_SIZE != (size_t) rc) | 
|  | die(_("Unable to read zone map")); | 
|  |  | 
|  | rc = read(IN, inode_buffer, buffsz); | 
|  | if (rc < 0 || buffsz != (size_t) rc) | 
|  | die(_("Unable to read inodes")); | 
|  | if (norm_first_zone != first_zone) { | 
|  | printf(_("Warning: Firstzone != Norm_firstzone\n")); | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | get_dirsize(); | 
|  | if (show) { | 
|  | printf(_("%ld inodes\n"), inodes); | 
|  | printf(_("%ld blocks\n"), zones); | 
|  | printf(_("Firstdatazone=%jd (%jd)\n"), first_zone, norm_first_zone); | 
|  | printf(_("Zonesize=%d\n"), MINIX_BLOCK_SIZE << get_zone_size()); | 
|  | printf(_("Maxsize=%zu\n"), get_max_size()); | 
|  | printf(_("Filesystem state=%d\n"), Super.s_state); | 
|  | printf(_("namelen=%zd\n\n"), namelen); | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct minix_inode * | 
|  | get_inode(unsigned int nr) { | 
|  | struct minix_inode *inode; | 
|  |  | 
|  | if (!nr || nr > get_ninodes()) | 
|  | return NULL; | 
|  | total++; | 
|  | inode = Inode + nr; | 
|  | if (!inode_count[nr]) { | 
|  | if (!inode_in_use(nr)) { | 
|  | get_current_name(); | 
|  | printf(_("Inode %d marked unused, " | 
|  | "but used for file '%s'\n"), nr, current_name); | 
|  | if (repair) { | 
|  | if (ask(_("Mark in use"), 1)) | 
|  | mark_inode(nr); | 
|  | } else { | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | } | 
|  | if (S_ISDIR(inode->i_mode)) | 
|  | directory++; | 
|  | else if (S_ISREG(inode->i_mode)) | 
|  | regular++; | 
|  | else if (S_ISCHR(inode->i_mode)) | 
|  | chardev++; | 
|  | else if (S_ISBLK(inode->i_mode)) | 
|  | blockdev++; | 
|  | else if (S_ISLNK(inode->i_mode)) | 
|  | symlinks++; | 
|  | else if (S_ISSOCK(inode->i_mode)) | 
|  | ; | 
|  | else if (S_ISFIFO(inode->i_mode)) | 
|  | ; | 
|  | else { | 
|  | get_current_name(); | 
|  | printf(_("The file `%s' has mode %05o\n"), | 
|  | current_name, inode->i_mode); | 
|  | } | 
|  |  | 
|  | } else | 
|  | links++; | 
|  | if (!++inode_count[nr]) { | 
|  | printf(_("Warning: inode count too big.\n")); | 
|  | inode_count[nr]--; | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | return inode; | 
|  | } | 
|  |  | 
|  | static struct minix2_inode * | 
|  | get_inode2(unsigned int nr) { | 
|  | struct minix2_inode *inode; | 
|  |  | 
|  | if (!nr || nr > get_ninodes()) | 
|  | return NULL; | 
|  | total++; | 
|  | inode = Inode2 + nr; | 
|  | if (!inode_count[nr]) { | 
|  | if (!inode_in_use(nr)) { | 
|  | get_current_name(); | 
|  | printf(_("Inode %d marked unused, " | 
|  | "but used for file '%s'\n"), nr, current_name); | 
|  | if (repair) { | 
|  | if (ask(_("Mark in use"), 1)) | 
|  | mark_inode(nr); | 
|  | else | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | } | 
|  | if (S_ISDIR(inode->i_mode)) | 
|  | directory++; | 
|  | else if (S_ISREG(inode->i_mode)) | 
|  | regular++; | 
|  | else if (S_ISCHR(inode->i_mode)) | 
|  | chardev++; | 
|  | else if (S_ISBLK(inode->i_mode)) | 
|  | blockdev++; | 
|  | else if (S_ISLNK(inode->i_mode)) | 
|  | symlinks++; | 
|  | else if (S_ISSOCK(inode->i_mode)) ; | 
|  | else if (S_ISFIFO(inode->i_mode)) ; | 
|  | else { | 
|  | get_current_name(); | 
|  | printf(_("The file `%s' has mode %05o\n"), | 
|  | current_name, inode->i_mode); | 
|  | } | 
|  | } else | 
|  | links++; | 
|  | if (!++inode_count[nr]) { | 
|  | printf(_("Warning: inode count too big.\n")); | 
|  | inode_count[nr]--; | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | return inode; | 
|  | } | 
|  |  | 
|  | static void | 
|  | check_root(void) { | 
|  | struct minix_inode *inode = Inode + ROOT_INO; | 
|  |  | 
|  | if (!inode || !S_ISDIR(inode->i_mode)) | 
|  | die(_("root inode isn't a directory")); | 
|  | } | 
|  |  | 
|  | static void | 
|  | check_root2(void) { | 
|  | struct minix2_inode *inode = Inode2 + ROOT_INO; | 
|  |  | 
|  | if (!inode || !S_ISDIR(inode->i_mode)) | 
|  | die(_("root inode isn't a directory")); | 
|  | } | 
|  |  | 
|  | static int | 
|  | add_zone(unsigned short *znr, int *corrected) { | 
|  | int block; | 
|  |  | 
|  | block = check_zone_nr(znr, corrected); | 
|  | if (!block) | 
|  | return 0; | 
|  | if (zone_count[block]) { | 
|  | get_current_name(); | 
|  | printf(_("Block has been used before. Now in file `%s'."), | 
|  | current_name); | 
|  | if (ask(_("Clear"), 1)) { | 
|  | *znr = 0; | 
|  | block = 0; | 
|  | *corrected = 1; | 
|  | } | 
|  | } | 
|  | if (!block) | 
|  | return 0; | 
|  | if (!zone_in_use(block)) { | 
|  | get_current_name(); | 
|  | printf(_("Block %d in file `%s' is marked not in use."), | 
|  | block, current_name); | 
|  | if (ask(_("Correct"), 1)) | 
|  | mark_zone(block); | 
|  | } | 
|  | if (!++zone_count[block]) | 
|  | zone_count[block]--; | 
|  | return block; | 
|  | } | 
|  |  | 
|  | static int | 
|  | add_zone2(unsigned int *znr, int *corrected) { | 
|  | int block; | 
|  |  | 
|  | block = check_zone_nr2(znr, corrected); | 
|  | if (!block) | 
|  | return 0; | 
|  | if (zone_count[block]) { | 
|  | get_current_name(); | 
|  | printf(_("Block has been used before. Now in file `%s'."), | 
|  | current_name); | 
|  | if (ask(_("Clear"), 1)) { | 
|  | *znr = 0; | 
|  | block = 0; | 
|  | *corrected = 1; | 
|  | } | 
|  | } | 
|  | if (!block) | 
|  | return 0; | 
|  | if (!zone_in_use(block)) { | 
|  | get_current_name(); | 
|  | printf(_("Block %d in file `%s' is marked not in use."), | 
|  | block, current_name); | 
|  | if (ask(_("Correct"), 1)) | 
|  | mark_zone(block); | 
|  | } | 
|  | if (!++zone_count[block]) | 
|  | zone_count[block]--; | 
|  | return block; | 
|  | } | 
|  |  | 
|  | static void | 
|  | add_zone_ind(unsigned short *znr, int *corrected) { | 
|  | static char blk[MINIX_BLOCK_SIZE]; | 
|  | int i, chg_blk = 0; | 
|  | int block; | 
|  |  | 
|  | block = add_zone(znr, corrected); | 
|  | if (!block) | 
|  | return; | 
|  | read_block(block, blk); | 
|  | for (i = 0; i < (MINIX_BLOCK_SIZE >> 1); i++) | 
|  | add_zone(i + (unsigned short *)blk, &chg_blk); | 
|  | if (chg_blk) | 
|  | write_block(block, blk); | 
|  | } | 
|  |  | 
|  | static void | 
|  | add_zone_ind2(unsigned int *znr, int *corrected) { | 
|  | static char blk[MINIX_BLOCK_SIZE]; | 
|  | int i, chg_blk = 0; | 
|  | int block; | 
|  |  | 
|  | block = add_zone2(znr, corrected); | 
|  | if (!block) | 
|  | return; | 
|  | read_block(block, blk); | 
|  | for (i = 0; i < MINIX_BLOCK_SIZE >> 2; i++) | 
|  | add_zone2(i + (unsigned int *)blk, &chg_blk); | 
|  | if (chg_blk) | 
|  | write_block(block, blk); | 
|  | } | 
|  |  | 
|  | static void | 
|  | add_zone_dind(unsigned short *znr, int *corrected) { | 
|  | static char blk[MINIX_BLOCK_SIZE]; | 
|  | int i, blk_chg = 0; | 
|  | int block; | 
|  |  | 
|  | block = add_zone(znr, corrected); | 
|  | if (!block) | 
|  | return; | 
|  | read_block(block, blk); | 
|  | for (i = 0; i < (MINIX_BLOCK_SIZE >> 1); i++) | 
|  | add_zone_ind(i + (unsigned short *)blk, &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, blk); | 
|  | } | 
|  |  | 
|  | static void | 
|  | add_zone_dind2(unsigned int *znr, int *corrected) { | 
|  | static char blk[MINIX_BLOCK_SIZE]; | 
|  | int i, blk_chg = 0; | 
|  | int block; | 
|  |  | 
|  | block = add_zone2(znr, corrected); | 
|  | if (!block) | 
|  | return; | 
|  | read_block(block, blk); | 
|  | for (i = 0; i < MINIX_BLOCK_SIZE >> 2; i++) | 
|  | add_zone_ind2(i + (unsigned int *)blk, &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, blk); | 
|  | } | 
|  |  | 
|  | static void | 
|  | add_zone_tind2(unsigned int *znr, int *corrected) { | 
|  | static char blk[MINIX_BLOCK_SIZE]; | 
|  | int i, blk_chg = 0; | 
|  | int block; | 
|  |  | 
|  | block = add_zone2(znr, corrected); | 
|  | if (!block) | 
|  | return; | 
|  | read_block(block, blk); | 
|  | for (i = 0; i < MINIX_BLOCK_SIZE >> 2; i++) | 
|  | add_zone_dind2(i + (unsigned int *)blk, &blk_chg); | 
|  | if (blk_chg) | 
|  | write_block(block, blk); | 
|  | } | 
|  |  | 
|  | static void | 
|  | check_zones(unsigned int i) { | 
|  | struct minix_inode *inode; | 
|  |  | 
|  | if (!i || i > get_ninodes()) | 
|  | return; | 
|  | if (inode_count[i] > 1)	/* have we counted this file already? */ | 
|  | return; | 
|  | inode = Inode + i; | 
|  | if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) && | 
|  | !S_ISLNK(inode->i_mode)) | 
|  | return; | 
|  | for (i = 0; i < 7; i++) | 
|  | add_zone(i + inode->i_zone, &changed); | 
|  | add_zone_ind(7 + inode->i_zone, &changed); | 
|  | add_zone_dind(8 + inode->i_zone, &changed); | 
|  | } | 
|  |  | 
|  | static void | 
|  | check_zones2(unsigned int i) { | 
|  | struct minix2_inode *inode; | 
|  |  | 
|  | if (!i || i > get_ninodes()) | 
|  | return; | 
|  | if (inode_count[i] > 1)	/* have we counted this file already? */ | 
|  | return; | 
|  | inode = Inode2 + i; | 
|  | if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) | 
|  | && !S_ISLNK(inode->i_mode)) | 
|  | return; | 
|  | for (i = 0; i < 7; i++) | 
|  | add_zone2(i + inode->i_zone, &changed); | 
|  | add_zone_ind2(7 + inode->i_zone, &changed); | 
|  | add_zone_dind2(8 + inode->i_zone, &changed); | 
|  | add_zone_tind2(9 + inode->i_zone, &changed); | 
|  | } | 
|  |  | 
|  | static void | 
|  | check_file(struct minix_inode *dir, unsigned int offset) { | 
|  | static char blk[MINIX_BLOCK_SIZE]; | 
|  | struct minix_inode *inode; | 
|  | unsigned int ino; | 
|  | char *name; | 
|  | int block; | 
|  |  | 
|  | block = map_block(dir, offset / MINIX_BLOCK_SIZE); | 
|  | read_block(block, blk); | 
|  | name = blk + (offset % MINIX_BLOCK_SIZE) + 2; | 
|  | ino = *(unsigned short *)(name - 2); | 
|  | if (ino > get_ninodes()) { | 
|  | get_current_name(); | 
|  | printf(_("The directory '%s' contains a bad inode number " | 
|  | "for file '%.*s'."), current_name, (int)namelen, name); | 
|  | if (ask(_(" Remove"), 1)) { | 
|  | *(unsigned short *)(name - 2) = 0; | 
|  | write_block(block, blk); | 
|  | } | 
|  | ino = 0; | 
|  | } | 
|  | if (name_depth < MAX_DEPTH) | 
|  | xstrncpy(name_list[name_depth], name, namelen); | 
|  | else | 
|  | return; | 
|  | name_depth++; | 
|  | inode = get_inode(ino); | 
|  | name_depth--; | 
|  | if (!offset) { | 
|  | if (!inode || strcmp(".", name)) { | 
|  | get_current_name(); | 
|  | printf(_("%s: bad directory: '.' isn't first\n"), | 
|  | current_name); | 
|  | errors_uncorrected = 1; | 
|  | } else | 
|  | return; | 
|  | } | 
|  | if (offset == dirsize) { | 
|  | if (!inode || strcmp("..", name)) { | 
|  | get_current_name(); | 
|  | printf(_("%s: bad directory: '..' isn't second\n"), | 
|  | current_name); | 
|  | errors_uncorrected = 1; | 
|  | } else | 
|  | return; | 
|  | } | 
|  | if (!inode) | 
|  | return; | 
|  | if (name_depth < MAX_DEPTH) | 
|  | xstrncpy(name_list[name_depth], name, namelen); | 
|  | else | 
|  | return; | 
|  | name_depth++; | 
|  | if (list) { | 
|  | if (verbose) | 
|  | printf("%6d %07o %3d ", ino, | 
|  | inode->i_mode, inode->i_nlinks); | 
|  | get_current_name(); | 
|  | printf("%s", current_name); | 
|  | if (S_ISDIR(inode->i_mode)) | 
|  | printf(":\n"); | 
|  | else | 
|  | printf("\n"); | 
|  | } | 
|  | check_zones(ino); | 
|  | if (inode && S_ISDIR(inode->i_mode)) | 
|  | recursive_check(ino); | 
|  | name_depth--; | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void | 
|  | check_file2(struct minix2_inode *dir, unsigned int offset) { | 
|  | static char blk[MINIX_BLOCK_SIZE]; | 
|  | struct minix2_inode *inode; | 
|  | ino_t ino; | 
|  | char *name; | 
|  | int block; | 
|  |  | 
|  | block = map_block2(dir, offset / MINIX_BLOCK_SIZE); | 
|  | read_block(block, blk); | 
|  | name = blk + (offset % MINIX_BLOCK_SIZE) + 2; | 
|  | ino = *(unsigned short *)(name - 2); | 
|  | if (ino > get_ninodes()) { | 
|  | get_current_name(); | 
|  | printf(_("The directory '%s' contains a bad inode number " | 
|  | "for file '%.*s'."), current_name, (int)namelen, name); | 
|  | if (ask(_(" Remove"), 1)) { | 
|  | *(unsigned short *)(name - 2) = 0; | 
|  | write_block(block, blk); | 
|  | } | 
|  | ino = 0; | 
|  | } | 
|  | if (name_depth < MAX_DEPTH) | 
|  | xstrncpy(name_list[name_depth], name, namelen); | 
|  | else | 
|  | return; | 
|  | name_depth++; | 
|  | inode = get_inode2(ino); | 
|  | name_depth--; | 
|  | if (!offset) { | 
|  | if (!inode || strcmp(".", name)) { | 
|  | get_current_name(); | 
|  | printf(_("%s: bad directory: '.' isn't first\n"), | 
|  | current_name); | 
|  | errors_uncorrected = 1; | 
|  | } else | 
|  | return; | 
|  | } | 
|  | if (offset == dirsize) { | 
|  | if (!inode || strcmp("..", name)) { | 
|  | get_current_name(); | 
|  | printf(_("%s: bad directory: '..' isn't second\n"), | 
|  | current_name); | 
|  | errors_uncorrected = 1; | 
|  | } else | 
|  | return; | 
|  | } | 
|  | if (!inode) | 
|  | return; | 
|  | name_depth++; | 
|  | if (list) { | 
|  | if (verbose) | 
|  | printf("%6ju %07o %3d ", ino, inode->i_mode, | 
|  | inode->i_nlinks); | 
|  | get_current_name(); | 
|  | printf("%s", current_name); | 
|  | if (S_ISDIR(inode->i_mode)) | 
|  | printf(":\n"); | 
|  | else | 
|  | printf("\n"); | 
|  | } | 
|  | check_zones2(ino); | 
|  | if (inode && S_ISDIR(inode->i_mode)) | 
|  | recursive_check2(ino); | 
|  | name_depth--; | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void | 
|  | recursive_check(unsigned int ino) { | 
|  | struct minix_inode *dir; | 
|  | off_t offset; | 
|  |  | 
|  | dir = Inode + ino; | 
|  | if (!S_ISDIR(dir->i_mode)) | 
|  | die(_("internal error")); | 
|  | if (dir->i_size < 2 * dirsize) { | 
|  | get_current_name(); | 
|  | printf(_("%s: bad directory: size < 32"), current_name); | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | for (offset = 0; offset < dir->i_size; offset += dirsize) | 
|  | check_file(dir, offset); | 
|  | } | 
|  |  | 
|  | static void | 
|  | recursive_check2(unsigned int ino) { | 
|  | struct minix2_inode *dir; | 
|  | off_t offset; | 
|  |  | 
|  | dir = Inode2 + ino; | 
|  | if (!S_ISDIR(dir->i_mode)) | 
|  | die(_("internal error")); | 
|  | if (dir->i_size < 2 * dirsize) { | 
|  | get_current_name(); | 
|  | printf(_("%s: bad directory: size < 32"), current_name); | 
|  | errors_uncorrected = 1; | 
|  | } | 
|  | for (offset = 0; offset < dir->i_size; offset += dirsize) | 
|  | check_file2(dir, offset); | 
|  | } | 
|  |  | 
|  | static int | 
|  | bad_zone(int i) { | 
|  | char buffer[1024]; | 
|  |  | 
|  | if (MINIX_BLOCK_SIZE * i != lseek(IN, MINIX_BLOCK_SIZE * i, SEEK_SET)) | 
|  | die(_("seek failed in bad_zone")); | 
|  | return (MINIX_BLOCK_SIZE != read(IN, buffer, MINIX_BLOCK_SIZE)); | 
|  | } | 
|  |  | 
|  | static void | 
|  | check_counts(void) { | 
|  | unsigned long i; | 
|  |  | 
|  | for (i = 1; i <= get_ninodes(); i++) { | 
|  | if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) { | 
|  | printf(_("Inode %lu mode not cleared."), i); | 
|  | if (ask(_("Clear"), 1)) { | 
|  | Inode[i].i_mode = 0; | 
|  | changed = 1; | 
|  | } | 
|  | } | 
|  | if (!inode_count[i]) { | 
|  | if (!inode_in_use(i)) | 
|  | continue; | 
|  | printf(_("Inode %lu not used, marked used in the bitmap."), i); | 
|  | if (ask(_("Clear"), 1)) | 
|  | unmark_inode(i); | 
|  | continue; | 
|  | } | 
|  | if (!inode_in_use(i)) { | 
|  | printf(_("Inode %lu used, marked unused in the bitmap."), i); | 
|  | if (ask(_("Set"), 1)) | 
|  | mark_inode(i); | 
|  | } | 
|  | if (Inode[i].i_nlinks != inode_count[i]) { | 
|  | printf(_("Inode %lu (mode = %07o), i_nlinks=%d, counted=%d."), | 
|  | i, Inode[i].i_mode, Inode[i].i_nlinks, | 
|  | inode_count[i]); | 
|  | if (ask(_("Set i_nlinks to count"), 1)) { | 
|  | Inode[i].i_nlinks = inode_count[i]; | 
|  | changed = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | for (i = get_first_zone(); i < get_nzones(); i++) { | 
|  | if (zone_in_use(i) == zone_count[i]) | 
|  | continue; | 
|  | if (!zone_count[i]) { | 
|  | if (bad_zone(i)) | 
|  | continue; | 
|  | printf(_("Zone %lu: marked in use, no file uses it."), | 
|  | i); | 
|  | if (ask(_("Unmark"), 1)) | 
|  | unmark_zone(i); | 
|  | continue; | 
|  | } | 
|  | if (zone_in_use(i)) | 
|  | printf(_("Zone %lu: in use, counted=%d\n"), | 
|  | i, zone_count[i]); | 
|  | else | 
|  | printf(_("Zone %lu: not in use, counted=%d\n"), | 
|  | i, zone_count[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | check_counts2(void) { | 
|  | unsigned long i; | 
|  |  | 
|  | for (i = 1; i <= get_ninodes(); i++) { | 
|  | if (!inode_in_use(i) && Inode2[i].i_mode && warn_mode) { | 
|  | printf(_("Inode %lu mode not cleared."), i); | 
|  | if (ask(_("Clear"), 1)) { | 
|  | Inode2[i].i_mode = 0; | 
|  | changed = 1; | 
|  | } | 
|  | } | 
|  | if (!inode_count[i]) { | 
|  | if (!inode_in_use(i)) | 
|  | continue; | 
|  | printf(_("Inode %lu not used, marked used in the bitmap."), i); | 
|  | if (ask(_("Clear"), 1)) | 
|  | unmark_inode(i); | 
|  | continue; | 
|  | } | 
|  | if (!inode_in_use(i)) { | 
|  | printf(_("Inode %lu used, marked unused in the bitmap."), i); | 
|  | if (ask(_("Set"), 1)) | 
|  | mark_inode(i); | 
|  | } | 
|  | if (Inode2[i].i_nlinks != inode_count[i]) { | 
|  | printf(_("Inode %lu (mode = %07o), i_nlinks=%d, counted=%d."), | 
|  | i, Inode2[i].i_mode, Inode2[i].i_nlinks, | 
|  | inode_count[i]); | 
|  | if (ask(_("Set i_nlinks to count"), 1)) { | 
|  | Inode2[i].i_nlinks = inode_count[i]; | 
|  | changed = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | for (i = get_first_zone(); i < get_nzones(); i++) { | 
|  | if (zone_in_use(i) == zone_count[i]) | 
|  | continue; | 
|  | if (!zone_count[i]) { | 
|  | if (bad_zone(i)) | 
|  | continue; | 
|  | printf(_("Zone %lu: marked in use, no file uses it."), | 
|  | i); | 
|  | if (ask(_("Unmark"), 1)) | 
|  | unmark_zone(i); | 
|  | continue; | 
|  | } | 
|  | if (zone_in_use(i)) | 
|  | printf(_("Zone %lu: in use, counted=%d\n"), | 
|  | i, zone_count[i]); | 
|  | else | 
|  | printf(_("Zone %lu: not in use, counted=%d\n"), | 
|  | i, zone_count[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | check(void) { | 
|  | memset(inode_count, 0, (get_ninodes() + 1) * sizeof(*inode_count)); | 
|  | memset(zone_count, 0, get_nzones() * sizeof(*zone_count)); | 
|  | check_zones(ROOT_INO); | 
|  | recursive_check(ROOT_INO); | 
|  | check_counts(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | check2(void) { | 
|  | memset(inode_count, 0, (get_ninodes() + 1) * sizeof(*inode_count)); | 
|  | memset(zone_count, 0, get_nzones() * sizeof(*zone_count)); | 
|  | check_zones2(ROOT_INO); | 
|  | recursive_check2(ROOT_INO); | 
|  | check_counts2(); | 
|  | } | 
|  |  | 
|  | int | 
|  | main(int argc, char **argv) { | 
|  | struct termios tmp; | 
|  | int count; | 
|  | int retcode = FSCK_EX_OK; | 
|  |  | 
|  | setlocale(LC_ALL, ""); | 
|  | bindtextdomain(PACKAGE, LOCALEDIR); | 
|  | textdomain(PACKAGE); | 
|  | atexit(close_stdout); | 
|  |  | 
|  | if (argc == 2 && | 
|  | (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version"))) { | 
|  | printf(UTIL_LINUX_VERSION); | 
|  | exit(FSCK_EX_OK); | 
|  | } | 
|  |  | 
|  | if (INODE_SIZE * MINIX_INODES_PER_BLOCK != MINIX_BLOCK_SIZE) | 
|  | die(_("bad inode size")); | 
|  | if (INODE2_SIZE * MINIX2_INODES_PER_BLOCK != MINIX_BLOCK_SIZE) | 
|  | die(_("bad v2 inode size")); | 
|  |  | 
|  | while (argc-- > 1) { | 
|  | argv++; | 
|  | if (argv[0][0] != '-') { | 
|  | if (device_name) | 
|  | usage(); | 
|  | else | 
|  | device_name = argv[0]; | 
|  | } else | 
|  | while (*++argv[0]) | 
|  | switch (argv[0][0]) { | 
|  | case 'l': | 
|  | list = 1; | 
|  | break; | 
|  | case 'a': | 
|  | automatic = 1; | 
|  | repair = 1; | 
|  | break; | 
|  | case 'r': | 
|  | automatic = 0; | 
|  | repair = 1; | 
|  | break; | 
|  | case 'v': | 
|  | verbose = 1; | 
|  | break; | 
|  | case 's': | 
|  | show = 1; | 
|  | break; | 
|  | case 'm': | 
|  | warn_mode = 1; | 
|  | break; | 
|  | case 'f': | 
|  | force = 1; | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | } | 
|  | } | 
|  | if (!device_name) | 
|  | usage(); | 
|  | check_mount();		/* trying to check a mounted filesystem? */ | 
|  | if (repair && !automatic) { | 
|  | if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) | 
|  | die(_("need terminal for interactive repairs")); | 
|  | } | 
|  | IN = open(device_name, repair ? O_RDWR : O_RDONLY); | 
|  | if (IN < 0) | 
|  | die(_("cannot open %s: %s"), device_name, strerror(errno)); | 
|  | for (count = 0; count < 3; count++) | 
|  | sync(); | 
|  | read_superblock(); | 
|  |  | 
|  | /* Determine whether or not we should continue with the checking.  This | 
|  | * is based on the status of the filesystem valid and error flags and | 
|  | * whether or not the -f switch was specified on the command line.  */ | 
|  | if (!(Super.s_state & MINIX_ERROR_FS) && | 
|  | (Super.s_state & MINIX_VALID_FS) && !force) { | 
|  | if (repair) | 
|  | printf(_("%s is clean, no check.\n"), device_name); | 
|  | return retcode; | 
|  | } else if (force) | 
|  | printf(_("Forcing filesystem check on %s.\n"), device_name); | 
|  | else if (repair) | 
|  | printf(_("Filesystem on %s is dirty, needs checking.\n"), | 
|  | device_name); | 
|  |  | 
|  | read_tables(); | 
|  |  | 
|  | /* Restore the terminal state on fatal signals.  We don't do this for | 
|  | * SIGALRM, SIGUSR1 or SIGUSR2.  */ | 
|  | signal(SIGINT, fatalsig); | 
|  | signal(SIGQUIT, fatalsig); | 
|  | signal(SIGTERM, fatalsig); | 
|  |  | 
|  | if (repair && !automatic) { | 
|  | tcgetattr(STDIN_FILENO, &termios); | 
|  | tmp = termios; | 
|  | tmp.c_lflag &= ~(ICANON | ECHO); | 
|  | tcsetattr(STDIN_FILENO, TCSANOW, &tmp); | 
|  | termios_set = 1; | 
|  | } | 
|  |  | 
|  | if (fs_version == 2) { | 
|  | check_root2(); | 
|  | check2(); | 
|  | } else { | 
|  | check_root(); | 
|  | check(); | 
|  | } | 
|  | if (verbose) { | 
|  | unsigned long i, free; | 
|  |  | 
|  | for (i = 1, free = 0; i <= get_ninodes(); i++) | 
|  | if (!inode_in_use(i)) | 
|  | free++; | 
|  | printf(_("\n%6ld inodes used (%ld%%)\n"), | 
|  | (get_ninodes() - free), | 
|  | 100 * (get_ninodes() - free) / get_ninodes()); | 
|  | for (i = get_first_zone(), free = 0; i < get_nzones(); i++) | 
|  | if (!zone_in_use(i)) | 
|  | free++; | 
|  | printf(_("%6ld zones used (%ld%%)\n"), (get_nzones() - free), | 
|  | 100 * (get_nzones() - free) / get_nzones()); | 
|  | printf(_("\n%6d regular files\n" | 
|  | "%6d directories\n" | 
|  | "%6d character device files\n" | 
|  | "%6d block device files\n" | 
|  | "%6d links\n" | 
|  | "%6d symbolic links\n" | 
|  | "------\n" | 
|  | "%6d files\n"), | 
|  | regular, directory, chardev, blockdev, | 
|  | links - 2 * directory + 1, symlinks, | 
|  | total - 2 * directory + 1); | 
|  | } | 
|  | if (changed) { | 
|  | write_tables(); | 
|  | printf(_("----------------------------\n" | 
|  | "FILE SYSTEM HAS BEEN CHANGED\n" | 
|  | "----------------------------\n")); | 
|  | for (count = 0; count < 3; count++) | 
|  | sync(); | 
|  | } else if (repair) | 
|  | write_super_block(); | 
|  |  | 
|  | if (repair && !automatic) | 
|  | tcsetattr(STDIN_FILENO, TCSANOW, &termios); | 
|  |  | 
|  | if (close_fd(IN) != 0) | 
|  | err(FSCK_EX_ERROR, _("write failed")); | 
|  | if (changed) | 
|  | retcode += 3; | 
|  | if (errors_uncorrected) | 
|  | retcode += 4; | 
|  | return retcode; | 
|  | } |