1  /*
       2   * fsck.minix.c - a file system consistency checker for Linux.
       3   *
       4   * (C) 1991, 1992 Linus Torvalds. This file may be redistributed
       5   * as per the GNU copyleft.
       6   */
       7  
       8  /*
       9   * 09.11.91  -  made the first rudimentary functions
      10   *
      11   * 10.11.91  -  updated, does checking, no repairs yet.
      12   *		Sent out to the mailing-list for testing.
      13   *
      14   * 14.11.91  -	Testing seems to have gone well. Added some
      15   *		correction-code, and changed some functions.
      16   *
      17   * 15.11.91  -  More correction code. Hopefully it notices most
      18   *		cases now, and tries to do something about them.
      19   *
      20   * 16.11.91  -  More corrections (thanks to Mika Jalava). Most
      21   *		things seem to work now. Yeah, sure.
      22   *
      23   *
      24   * 19.04.92  -	Had to start over again from this old version, as a
      25   *		kernel bug ate my enhanced fsck in February.
      26   *
      27   * 28.02.93  -	added support for different directory entry sizes..
      28   *
      29   * Sat Mar  6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
      30   *                           super-block information
      31   *
      32   * Sat Oct  9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
      33   *                           to that required by fsutil
      34   *
      35   * Mon Jan  3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
      36   *			      Added support for file system valid flag.  Also
      37   *			      added program_version variable and output of
      38   *			      program name and version number when program
      39   *			      is executed.
      40   *
      41   * 30.10.94 - added support for v2 filesystem
      42   *            (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
      43   *
      44   * 10.12.94  -  added test to prevent checking of mounted fs adapted
      45   *              from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
      46   *              program.  (Daniel Quinlan, quinlan@yggdrasil.com)
      47   *
      48   * 01.07.96  - Fixed the v2 fs stuff to use the right #defines and such
      49   *	       for modern libcs (janl@math.uio.no, Nicolai Langfeldt)
      50   *
      51   * 02.07.96  - Added C bit fiddling routines from rmk@ecs.soton.ac.uk
      52   *             (Russell King).  He made them for ARM.  It would seem
      53   *	       that the ARM is powerful enough to do this in C whereas
      54   *             i386 and m64k must use assembly to get it fast >:-)
      55   *	       This should make minix fsck systemindependent.
      56   *	       (janl@math.uio.no, Nicolai Langfeldt)
      57   *
      58   * 04.11.96  - Added minor fixes from Andreas Schwab to avoid compiler
      59   *             warnings.  Added mc68k bitops from
      60   *	       Joerg Dorchain <dorchain@mpi-sb.mpg.de>.
      61   *
      62   * 06.11.96  - Added v2 code submitted by Joerg Dorchain, but written by
      63   *             Andreas Schwab.
      64   *
      65   * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
      66   * - added Native Language Support
      67   *
      68   * 2008-04-06 James Youngman <jay@gnu.org>
      69   * - Issue better error message if we fail to open the device.
      70   * - Restore terminal state if we get a fatal signal.
      71   *
      72   *
      73   * I've had no time to add comments - hopefully the function names
      74   * are comments enough. As with all file system checkers, this assumes
      75   * the file system is quiescent - don't use it on a mounted device
      76   * unless you can be sure nobody is writing to it (and remember that the
      77   * kernel can write to it when it searches for files).
      78   *
      79   */
      80  
      81  #include <stdio.h>
      82  #include <stdarg.h>
      83  #include <errno.h>
      84  #include <unistd.h>
      85  #include <string.h>
      86  #include <fcntl.h>
      87  #include <ctype.h>
      88  #include <stdlib.h>
      89  #include <termios.h>
      90  #include <sys/stat.h>
      91  #include <signal.h>
      92  #include <getopt.h>
      93  
      94  #include "c.h"
      95  #include "exitcodes.h"
      96  #include "minix_programs.h"
      97  #include "nls.h"
      98  #include "pathnames.h"
      99  #include "bitops.h"
     100  #include "ismounted.h"
     101  #include "all-io.h"
     102  #include "closestream.h"
     103  #include "rpmatch.h"
     104  #include "strutils.h"
     105  
     106  #define ROOT_INO 1
     107  #define YESNO_LENGTH 64
     108  
     109  /* Global variables used in minix_programs.h inline functions */
     110  int fs_version = 1;
     111  char *super_block_buffer;
     112  
     113  static char *inode_buffer;
     114  
     115  #define Inode (((struct minix_inode *) inode_buffer) - 1)
     116  #define Inode2 (((struct minix2_inode *) inode_buffer) - 1)
     117  
     118  static char *device_name;
     119  static int device_fd;
     120  static int repair, automatic, verbose, list, show, warn_mode, force;
     121  static int directory, regular, blockdev, chardev, links, symlinks, total;
     122  
     123  static int changed;		/* flags if the filesystem has been changed */
     124  static int errors_uncorrected;	/* flag if some error was not corrected */
     125  static size_t dirsize = 16;
     126  static size_t namelen = 14;
     127  static struct termios termios;
     128  static volatile sig_atomic_t termios_set;
     129  
     130  /* File-name data */
     131  #define MAX_DEPTH 50
     132  static int name_depth;
     133  static char name_list[MAX_DEPTH][MINIX_NAME_MAX + 1];
     134  
     135  /* Copy of the previous, just for error reporting - see get_current_name.  This
     136   * is a waste of 12kB or so.  */
     137  static char current_name[MAX_DEPTH * (MINIX_NAME_MAX + 1) + 1];
     138  
     139  static unsigned char *inode_count = NULL;
     140  static unsigned char *zone_count = NULL;
     141  
     142  static void recursive_check(unsigned int ino);
     143  static void recursive_check2(unsigned int ino);
     144  
     145  static char *inode_map;
     146  static char *zone_map;
     147  
     148  #define inode_in_use(x) (isset(inode_map,(x)) != 0)
     149  #define zone_in_use(x) (isset(zone_map,(x)-get_first_zone()+1) != 0)
     150  
     151  #define mark_inode(x) (setbit(inode_map,(x)),changed=1)
     152  #define unmark_inode(x) (clrbit(inode_map,(x)),changed=1)
     153  
     154  #define mark_zone(x) (setbit(zone_map,(x)-get_first_zone()+1),changed=1)
     155  #define unmark_zone(x) (clrbit(zone_map,(x)-get_first_zone()+1),changed=1)
     156  
     157  static void
     158  reset(void) {
     159  	if (termios_set)
     160  		tcsetattr(STDIN_FILENO, TCSANOW, &termios);
     161  }
     162  
     163  static void
     164  fatalsig(int sig) {
     165  	/* We received a fatal signal.  Reset the terminal.  Also reset the
     166  	 * signal handler and re-send the signal, so that the parent process
     167  	 * knows which signal actually caused our death.  */
     168  	signal(sig, SIG_DFL);
     169  	reset();
     170  	raise(sig);
     171  }
     172  
     173  static void __attribute__((__noreturn__))
     174  leave(int status) {
     175  	reset();
     176  	exit(status);
     177  }
     178  
     179  static void __attribute__((__noreturn__))
     180  usage(void) {
     181  	FILE *out = stdout;
     182  	fputs(USAGE_HEADER, out);
     183  	fprintf(out, _(" %s [options] <device>\n"), program_invocation_short_name);
     184  	fputs(USAGE_SEPARATOR, out);
     185  	fputs(_("Check the consistency of a Minix filesystem.\n"), out);
     186  	fputs(USAGE_OPTIONS, out);
     187  	fputs(_(" -l, --list       list all filenames\n"), out);
     188  	fputs(_(" -a, --auto       automatic repair\n"), out);
     189  	fputs(_(" -r, --repair     interactive repair\n"), out);
     190  	fputs(_(" -v, --verbose    be verbose\n"), out);
     191  	fputs(_(" -s, --super      output super-block information\n"), out);
     192  	fputs(_(" -m, --uncleared  activate mode not cleared warnings\n"), out);
     193  	fputs(_(" -f, --force      force check\n"), out);
     194  	fputs(USAGE_SEPARATOR, out);
     195  	printf(USAGE_HELP_OPTIONS(18));
     196  	printf(USAGE_MAN_TAIL("fsck.minix(8)"));
     197  	exit(FSCK_EX_OK);
     198  }
     199  
     200  static void die(const char *fmt, ...)
     201      __attribute__ ((__format__(__printf__, 1, 2)));
     202  
     203  static void
     204  die(const char *fmt, ...) {
     205  	va_list ap;
     206  
     207  	fprintf(stderr, UTIL_LINUX_VERSION);
     208  	va_start(ap, fmt);
     209  	vfprintf(stderr, fmt, ap);
     210  	va_end(ap);
     211  	fputc('\n', stderr);
     212  	leave(FSCK_EX_ERROR);
     213  }
     214  
     215  /* This simply goes through the file-name data and prints out the current file.  */
     216  static void
     217  get_current_name(void) {
     218  	int i = 0, ct;
     219  	char *p, *q;
     220  
     221  	q = current_name;
     222  	while (i < name_depth) {
     223  		p = name_list[i++];
     224  		ct = namelen;
     225  		*q++ = '/';
     226  		while (ct-- && *p)
     227  			*q++ = *p++;
     228  	}
     229  	if (i == 0)
     230  		*q++ = '/';
     231  	*q = 0;
     232  }
     233  
     234  static int
     235  ask(const char *string, int def) {
     236  	int resp;
     237  	char input[YESNO_LENGTH];
     238  
     239  	if (!repair) {
     240  		printf("\n");
     241  		errors_uncorrected = 1;
     242  		return 0;
     243  	}
     244  	if (automatic) {
     245  		printf("\n");
     246  		if (!def)
     247  			errors_uncorrected = 1;
     248  		return def;
     249  	}
     250  	/* TRANSLATORS: these yes no questions uses rpmatch(), and should be
     251  	 * translated.  */
     252  	printf(def ? _("%s (y/n)? ") : _("%s (n/y)? "), string);
     253  	fflush(stdout);
     254  	ignore_result( fgets(input, YESNO_LENGTH, stdin) );
     255  	resp = rpmatch(input);
     256  	switch (resp) {
     257  	case RPMATCH_INVALID:
     258  		/* def = def */
     259  		break;
     260  	case RPMATCH_NO:
     261  	case RPMATCH_YES:
     262  		def = resp;
     263  		break;
     264  	default:
     265  		/* rpmatch bug? */
     266  		abort();
     267  	}
     268  	if (def)
     269  		printf(_("y\n"));
     270  	else {
     271  		printf(_("n\n"));
     272  		errors_uncorrected = 1;
     273  	}
     274  	return def;
     275  }
     276  
     277  /* Make certain that we aren't checking a filesystem that is on a mounted
     278   * partition.  Code adapted from e2fsck, Copyright (C) 1993, 1994 Theodore
     279   * Ts'o.  Also licensed under GPL.  */
     280  static void
     281  check_mount(void) {
     282  	int cont;
     283  
     284  	if (!is_mounted(device_name))
     285  		return;
     286  
     287  	printf(_("%s is mounted.	 "), device_name);
     288  	if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
     289  		cont = ask(_("Do you really want to continue"), 0);
     290  	else
     291  		cont = 0;
     292  	if (!cont) {
     293  		printf(_("check aborted.\n"));
     294  		exit(FSCK_EX_OK);
     295  	}
     296  }
     297  
     298  
     299  static int is_valid_zone_nr(unsigned short nr)
     300  {
     301  	if (nr < get_first_zone())
     302  		return 0;
     303  	if (nr >= get_nzones())
     304  		return 0;
     305  	return 1;
     306  }
     307  
     308  /* check_zone_nr checks to see that *nr is a valid zone nr.  If it isn't, it
     309   * will possibly be repaired.  Check_zone_nr sets *corrected if an error was
     310   * corrected, and returns the zone (0 for no zone or a bad zone-number).  */
     311  static int
     312  check_zone_nr(unsigned short *nr, int *corrected) {
     313  	if (!*nr)
     314  		return 0;
     315  
     316  	if (*nr < get_first_zone()) {
     317  		get_current_name();
     318  		printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name);
     319  	} else if (*nr >= get_nzones()) {
     320  		get_current_name();
     321  		printf(_("Zone nr >= ZONES in file `%s'."), current_name);
     322  	} else
     323  		return *nr;
     324  
     325  	if (ask(_("Remove block"), 1)) {
     326  		*nr = 0;
     327  		*corrected = 1;
     328  	}
     329  	return 0;
     330  }
     331  
     332  static int
     333  check_zone_nr2(unsigned int *nr, int *corrected) {
     334  	if (!*nr)
     335  		return 0;
     336  
     337  	if (*nr < get_first_zone()) {
     338  		get_current_name();
     339  		printf(_("Zone nr < FIRSTZONE in file `%s'."), current_name);
     340  	} else if (*nr >= get_nzones()) {
     341  		get_current_name();
     342  		printf(_("Zone nr >= ZONES in file `%s'."), current_name);
     343  	} else
     344  		return *nr;
     345  
     346  	if (ask(_("Remove block"), 1)) {
     347  		*nr = 0;
     348  		*corrected = 1;
     349  	}
     350  	return 0;
     351  }
     352  
     353  /* read-block reads block nr into the buffer at addr.  */
     354  static void
     355  read_block(unsigned int nr, char *addr) {
     356  	if (!nr) {
     357  		memset(addr, 0, MINIX_BLOCK_SIZE);
     358  		return;
     359  	}
     360  	if (MINIX_BLOCK_SIZE * nr != lseek(device_fd, MINIX_BLOCK_SIZE * nr, SEEK_SET)) {
     361  		get_current_name();
     362  		printf(_("Read error: unable to seek to block in file '%s'\n"),
     363  		       current_name);
     364  		memset(addr, 0, MINIX_BLOCK_SIZE);
     365  		errors_uncorrected = 1;
     366  	} else if (MINIX_BLOCK_SIZE != read(device_fd, addr, MINIX_BLOCK_SIZE)) {
     367  		get_current_name();
     368  		printf(_("Read error: bad block in file '%s'\n"), current_name);
     369  		memset(addr, 0, MINIX_BLOCK_SIZE);
     370  		errors_uncorrected = 1;
     371  	}
     372  }
     373  
     374  /* write_block writes block nr to disk.  */
     375  static void
     376  write_block(unsigned int nr, char *addr) {
     377  	if (!nr)
     378  		return;
     379  	if (nr < get_first_zone() || nr >= get_nzones()) {
     380  		printf(_("Internal error: trying to write bad block\n"
     381  			 "Write request ignored\n"));
     382  		errors_uncorrected = 1;
     383  		return;
     384  	}
     385  	if (MINIX_BLOCK_SIZE * nr != lseek(device_fd, MINIX_BLOCK_SIZE * nr, SEEK_SET))
     386  		die(_("seek failed in write_block"));
     387  	if (MINIX_BLOCK_SIZE != write(device_fd, addr, MINIX_BLOCK_SIZE)) {
     388  		get_current_name();
     389  		printf(_("Write error: bad block in file '%s'\n"),
     390  		       current_name);
     391  		errors_uncorrected = 1;
     392  	}
     393  }
     394  
     395  /* map-block calculates the absolute block nr of a block in a file.  It sets
     396   * 'changed' if the inode has needed changing, and re-writes any indirect
     397   * blocks with errors.  */
     398  static int
     399  map_block(struct minix_inode *inode, unsigned int blknr) {
     400  	unsigned short ind[MINIX_BLOCK_SIZE >> 1];
     401  	unsigned short dind[MINIX_BLOCK_SIZE >> 1];
     402  	int blk_chg, block, result;
     403  	size_t range;
     404  
     405  	if (blknr < 7)
     406  		return check_zone_nr(inode->i_zone + blknr, &changed);
     407  	blknr -= 7;
     408  	if (blknr < 512) {
     409  		block = check_zone_nr(inode->i_zone + 7, &changed);
     410  		read_block(block, (char *)ind);
     411  		blk_chg = 0;
     412  		result = check_zone_nr(blknr + ind, &blk_chg);
     413  		if (blk_chg)
     414  			write_block(block, (char *)ind);
     415  		return result;
     416  	}
     417  	blknr -= 512;
     418  	block = check_zone_nr(inode->i_zone + 8, &changed);
     419  	read_block(block, (char *)dind);
     420  	blk_chg = 0;
     421  	range = blknr / 512;
     422  	if (ARRAY_SIZE(dind) <= range) {
     423  		printf(_("Warning: block out of range\n"));
     424  		return 1;
     425  	}
     426  	result = check_zone_nr(dind + range, &blk_chg);
     427  	if (blk_chg)
     428  		write_block(block, (char *)dind);
     429  	block = result;
     430  	read_block(block, (char *)ind);
     431  	blk_chg = 0;
     432  	result = check_zone_nr(ind + (blknr % 512), &blk_chg);
     433  	if (blk_chg)
     434  		write_block(block, (char *)ind);
     435  	return result;
     436  }
     437  
     438  static int
     439  map_block2(struct minix2_inode *inode, unsigned int blknr) {
     440  	unsigned int ind[MINIX_BLOCK_SIZE >> 2];
     441  	unsigned int dind[MINIX_BLOCK_SIZE >> 2];
     442  	unsigned int tind[MINIX_BLOCK_SIZE >> 2];
     443  	int blk_chg, block, result;
     444  
     445  	if (blknr < 7)
     446  		return check_zone_nr2(inode->i_zone + blknr, &changed);
     447  	blknr -= 7;
     448  	if (blknr < 256) {
     449  		block = check_zone_nr2(inode->i_zone + 7, &changed);
     450  		read_block(block, (char *)ind);
     451  		blk_chg = 0;
     452  		result = check_zone_nr2(blknr + ind, &blk_chg);
     453  		if (blk_chg)
     454  			write_block(block, (char *)ind);
     455  		return result;
     456  	}
     457  	blknr -= 256;
     458  	if (blknr < 256 * 256) {
     459  		block = check_zone_nr2(inode->i_zone + 8, &changed);
     460  		read_block(block, (char *)dind);
     461  		blk_chg = 0;
     462  		result = check_zone_nr2(dind + blknr / 256, &blk_chg);
     463  		if (blk_chg)
     464  			write_block(block, (char *)dind);
     465  		block = result;
     466  		read_block(block, (char *)ind);
     467  		blk_chg = 0;
     468  		result = check_zone_nr2(ind + blknr % 256, &blk_chg);
     469  		if (blk_chg)
     470  			write_block(block, (char *)ind);
     471  		return result;
     472  	}
     473  	blknr -= 256 * 256;
     474  	block = check_zone_nr2(inode->i_zone + 9, &changed);
     475  	read_block(block, (char *)tind);
     476  	blk_chg = 0;
     477  	result = check_zone_nr2(tind + blknr / (256 * 256), &blk_chg);
     478  	if (blk_chg)
     479  		write_block(block, (char *)tind);
     480  	block = result;
     481  	read_block(block, (char *)dind);
     482  	blk_chg = 0;
     483  	result = check_zone_nr2(dind + (blknr / 256) % 256, &blk_chg);
     484  	if (blk_chg)
     485  		write_block(block, (char *)dind);
     486  	block = result;
     487  	read_block(block, (char *)ind);
     488  	blk_chg = 0;
     489  	result = check_zone_nr2(ind + blknr % 256, &blk_chg);
     490  	if (blk_chg)
     491  		write_block(block, (char *)ind);
     492  	return result;
     493  }
     494  
     495  static void
     496  write_super_block(void) {
     497  	/* v3 super block does not track state */
     498  	if (fs_version == 3)
     499  		return;
     500  	/* Set the state of the filesystem based on whether or not there are
     501  	 * uncorrected errors.  The filesystem valid flag is unconditionally
     502  	 * set if we get this far.  */
     503  	Super.s_state |= MINIX_VALID_FS;
     504  	if (errors_uncorrected)
     505  		Super.s_state |= MINIX_ERROR_FS;
     506  	else
     507  		Super.s_state &= ~MINIX_ERROR_FS;
     508  
     509  	if (MINIX_BLOCK_SIZE != lseek(device_fd, MINIX_BLOCK_SIZE, SEEK_SET))
     510  		die(_("seek failed in write_super_block"));
     511  	if (MINIX_BLOCK_SIZE != write(device_fd, super_block_buffer, MINIX_BLOCK_SIZE))
     512  		die(_("unable to write super-block"));
     513  }
     514  
     515  static void
     516  write_tables(void) {
     517  	unsigned long buffsz = get_inode_buffer_size();
     518  	unsigned long imaps = get_nimaps();
     519  	unsigned long zmaps = get_nzmaps();
     520  
     521  	write_super_block();
     522  
     523  	if (write_all(device_fd, inode_map, imaps * MINIX_BLOCK_SIZE))
     524  		die(_("Unable to write inode map"));
     525  
     526  	if (write_all(device_fd, zone_map, zmaps * MINIX_BLOCK_SIZE))
     527  		die(_("Unable to write zone map"));
     528  
     529  	if (write_all(device_fd, inode_buffer, buffsz))
     530  		die(_("Unable to write inodes"));
     531  }
     532  
     533  static void
     534  get_dirsize(void) {
     535  	int block;
     536  	char blk[MINIX_BLOCK_SIZE];
     537  	size_t size;
     538  
     539  	if (fs_version == 2 || fs_version == 3)
     540  		block = Inode2[ROOT_INO].i_zone[0];
     541  	else
     542  		block = Inode[ROOT_INO].i_zone[0];
     543  	read_block(block, blk);
     544  
     545  	for (size = 16; size < MINIX_BLOCK_SIZE; size <<= 1) {
     546  		if (strcmp(blk + size + 2, "..") == 0) {
     547  			dirsize = size;
     548  			namelen = size - 2;
     549  			return;
     550  		}
     551  	}
     552  	/* use defaults */
     553  }
     554  
     555  static void
     556  read_superblock(void) {
     557  	if (MINIX_BLOCK_SIZE != lseek(device_fd, MINIX_BLOCK_SIZE, SEEK_SET))
     558  		die(_("seek failed"));
     559  
     560  	super_block_buffer = calloc(1, MINIX_BLOCK_SIZE);
     561  	if (!super_block_buffer)
     562  		die(_("unable to alloc buffer for superblock"));
     563  
     564  	if (MINIX_BLOCK_SIZE != read(device_fd, super_block_buffer, MINIX_BLOCK_SIZE))
     565  		die(_("unable to read super block"));
     566  	if (Super.s_magic == MINIX_SUPER_MAGIC) {
     567  		namelen = 14;
     568  		dirsize = 16;
     569  		fs_version = 1;
     570  	} else if (Super.s_magic == MINIX_SUPER_MAGIC2) {
     571  		namelen = 30;
     572  		dirsize = 32;
     573  		fs_version = 1;
     574  	} else if (Super.s_magic == MINIX2_SUPER_MAGIC) {
     575  		namelen = 14;
     576  		dirsize = 16;
     577  		fs_version = 2;
     578  	} else if (Super.s_magic == MINIX2_SUPER_MAGIC2) {
     579  		namelen = 30;
     580  		dirsize = 32;
     581  		fs_version = 2;
     582  	} else if (Super3.s_magic == MINIX3_SUPER_MAGIC) {
     583  		namelen = 60;
     584  		dirsize = 64;
     585  		fs_version = 3;
     586  	} else
     587  		die(_("bad magic number in super-block"));
     588  	if (get_zone_size() != 0 || MINIX_BLOCK_SIZE != 1024)
     589  		die(_("Only 1k blocks/zones supported"));
     590  	if (get_ninodes() == 0 || get_ninodes() == UINT32_MAX)
     591  		die(_("bad s_ninodes field in super-block"));
     592  	if (get_nimaps() * MINIX_BLOCK_SIZE * 8 < get_ninodes() + 1)
     593  		die(_("bad s_imap_blocks field in super-block"));
     594  	if (get_first_zone() > (off_t) get_nzones())
     595  		die(_("bad s_firstdatazone field in super-block"));
     596  	if (get_nzmaps() * MINIX_BLOCK_SIZE * 8 <
     597  	    get_nzones() - get_first_zone() + 1)
     598  		die(_("bad s_zmap_blocks field in super-block"));
     599  }
     600  
     601  static void
     602  read_tables(void) {
     603  	unsigned long inodes = get_ninodes();
     604  	size_t buffsz = get_inode_buffer_size();
     605  	off_t norm_first_zone = first_zone_data();
     606  	off_t first_zone = get_first_zone();
     607  	unsigned long zones = get_nzones();
     608  	unsigned long imaps = get_nimaps();
     609  	unsigned long zmaps = get_nzmaps();
     610  	ssize_t rc;
     611  
     612  	inode_map = malloc(imaps * MINIX_BLOCK_SIZE);
     613  	if (!inode_map)
     614  		die(_("Unable to allocate buffer for inode map"));
     615  	zone_map = malloc(zmaps * MINIX_BLOCK_SIZE);
     616  	if (!zone_map)
     617  		die(_("Unable to allocate buffer for zone map"));
     618  	inode_buffer = malloc(buffsz);
     619  	if (!inode_buffer)
     620  		die(_("Unable to allocate buffer for inodes"));
     621  	inode_count = calloc(1, inodes + 1);
     622  	if (!inode_count)
     623  		die(_("Unable to allocate buffer for inode count"));
     624  	zone_count = calloc(1, zones);
     625  	if (!zone_count)
     626  		die(_("Unable to allocate buffer for zone count"));
     627  
     628  	rc = read(device_fd, inode_map, imaps * MINIX_BLOCK_SIZE);
     629  	if (rc < 0 || imaps * MINIX_BLOCK_SIZE != (size_t) rc)
     630  		die(_("Unable to read inode map"));
     631  
     632  	rc = read(device_fd, zone_map, zmaps * MINIX_BLOCK_SIZE);
     633  	if (rc < 0 || zmaps * MINIX_BLOCK_SIZE != (size_t) rc)
     634  		die(_("Unable to read zone map"));
     635  
     636  	rc = read(device_fd, inode_buffer, buffsz);
     637  	if (rc < 0 || buffsz != (size_t) rc)
     638  		die(_("Unable to read inodes"));
     639  	if (norm_first_zone != first_zone) {
     640  		printf(_("Warning: Firstzone != Norm_firstzone\n"));
     641  		errors_uncorrected = 1;
     642  	}
     643  	get_dirsize();
     644  	if (show) {
     645  		printf(_("%ld inodes\n"), inodes);
     646  		printf(_("%ld blocks\n"), zones);
     647  		printf(_("Firstdatazone=%jd (%jd)\n"),
     648  			(intmax_t)first_zone, (intmax_t)norm_first_zone);
     649  		printf(_("Zonesize=%d\n"), MINIX_BLOCK_SIZE << get_zone_size());
     650  		printf(_("Maxsize=%zu\n"), get_max_size());
     651  		if (fs_version < 3)
     652  			printf(_("Filesystem state=%d\n"), Super.s_state);
     653  		printf(_("namelen=%zd\n\n"), namelen);
     654  	}
     655  }
     656  
     657  static struct minix_inode *
     658  get_inode(unsigned int nr) {
     659  	struct minix_inode *inode;
     660  
     661  	if (!nr || nr > get_ninodes())
     662  		return NULL;
     663  	total++;
     664  	inode = Inode + nr;
     665  	if (!inode_count[nr]) {
     666  		if (!inode_in_use(nr)) {
     667  			get_current_name();
     668  			printf(_("Inode %d marked unused, "
     669  				 "but used for file '%s'\n"), nr, current_name);
     670  			if (repair) {
     671  				if (ask(_("Mark in use"), 1))
     672  					mark_inode(nr);
     673  			} else {
     674  				errors_uncorrected = 1;
     675  			}
     676  		}
     677  		if (S_ISDIR(inode->i_mode))
     678  			directory++;
     679  		else if (S_ISREG(inode->i_mode))
     680  			regular++;
     681  		else if (S_ISCHR(inode->i_mode))
     682  			chardev++;
     683  		else if (S_ISBLK(inode->i_mode))
     684  			blockdev++;
     685  		else if (S_ISLNK(inode->i_mode))
     686  			symlinks++;
     687  		else if (S_ISSOCK(inode->i_mode))
     688  			;
     689  		else if (S_ISFIFO(inode->i_mode))
     690  			;
     691  		else {
     692  			get_current_name();
     693  			printf(_("The file `%s' has mode %05o\n"),
     694  			       current_name, inode->i_mode);
     695  		}
     696  
     697  	} else
     698  		links++;
     699  	if (!++inode_count[nr]) {
     700  		printf(_("Warning: inode count too big.\n"));
     701  		inode_count[nr]--;
     702  		errors_uncorrected = 1;
     703  	}
     704  	return inode;
     705  }
     706  
     707  static struct minix2_inode *
     708  get_inode2(unsigned int nr) {
     709  	struct minix2_inode *inode;
     710  
     711  	if (!nr || nr > get_ninodes())
     712  		return NULL;
     713  	total++;
     714  	inode = Inode2 + nr;
     715  	if (!inode_count[nr]) {
     716  		if (!inode_in_use(nr)) {
     717  			get_current_name();
     718  			printf(_("Inode %d marked unused, "
     719  				 "but used for file '%s'\n"), nr, current_name);
     720  			if (repair) {
     721  				if (ask(_("Mark in use"), 1))
     722  					mark_inode(nr);
     723  				else
     724  					errors_uncorrected = 1;
     725  			}
     726  		}
     727  		if (S_ISDIR(inode->i_mode))
     728  			directory++;
     729  		else if (S_ISREG(inode->i_mode))
     730  			regular++;
     731  		else if (S_ISCHR(inode->i_mode))
     732  			chardev++;
     733  		else if (S_ISBLK(inode->i_mode))
     734  			blockdev++;
     735  		else if (S_ISLNK(inode->i_mode))
     736  			symlinks++;
     737  		else if (S_ISSOCK(inode->i_mode)) ;
     738  		else if (S_ISFIFO(inode->i_mode)) ;
     739  		else {
     740  			get_current_name();
     741  			printf(_("The file `%s' has mode %05o\n"),
     742  			       current_name, inode->i_mode);
     743  		}
     744  	} else
     745  		links++;
     746  	if (!++inode_count[nr]) {
     747  		printf(_("Warning: inode count too big.\n"));
     748  		inode_count[nr]--;
     749  		errors_uncorrected = 1;
     750  	}
     751  	return inode;
     752  }
     753  
     754  static void
     755  check_root(void) {
     756  	struct minix_inode *inode = Inode + ROOT_INO;
     757  
     758  	if (!inode || !S_ISDIR(inode->i_mode))
     759  		die(_("root inode isn't a directory"));
     760  }
     761  
     762  static void
     763  check_root2(void) {
     764  	struct minix2_inode *inode = Inode2 + ROOT_INO;
     765  
     766  	if (!inode || !S_ISDIR(inode->i_mode))
     767  		die(_("root inode isn't a directory"));
     768  }
     769  
     770  static int
     771  add_zone(unsigned short *znr, int *corrected) {
     772  	int block;
     773  
     774  	block = check_zone_nr(znr, corrected);
     775  	if (!block)
     776  		return 0;
     777  	if (zone_count[block]) {
     778  		get_current_name();
     779  		printf(_("Block has been used before. Now in file `%s'."),
     780  		       current_name);
     781  		if (ask(_("Clear"), 1)) {
     782  			*znr = 0;
     783  			block = 0;
     784  			*corrected = 1;
     785  		}
     786  	}
     787  	if (!block)
     788  		return 0;
     789  	if (!zone_in_use(block)) {
     790  		get_current_name();
     791  		printf(_("Block %d in file `%s' is marked not in use."),
     792  		       block, current_name);
     793  		if (ask(_("Correct"), 1))
     794  			mark_zone(block);
     795  	}
     796  	if (!++zone_count[block])
     797  		zone_count[block]--;
     798  	return block;
     799  }
     800  
     801  static int
     802  add_zone2(unsigned int *znr, int *corrected) {
     803  	int block;
     804  
     805  	block = check_zone_nr2(znr, corrected);
     806  	if (!block)
     807  		return 0;
     808  	if (zone_count[block]) {
     809  		get_current_name();
     810  		printf(_("Block has been used before. Now in file `%s'."),
     811  		       current_name);
     812  		if (ask(_("Clear"), 1)) {
     813  			*znr = 0;
     814  			block = 0;
     815  			*corrected = 1;
     816  		}
     817  	}
     818  	if (!block)
     819  		return 0;
     820  	if (!zone_in_use(block)) {
     821  		get_current_name();
     822  		printf(_("Block %d in file `%s' is marked not in use."),
     823  		       block, current_name);
     824  		if (ask(_("Correct"), 1))
     825  			mark_zone(block);
     826  	}
     827  	if (!++zone_count[block])
     828  		zone_count[block]--;
     829  	return block;
     830  }
     831  
     832  static void
     833  add_zone_ind(unsigned short *znr, int *corrected) {
     834  	static char blk[MINIX_BLOCK_SIZE];
     835  	int i, chg_blk = 0;
     836  	int block;
     837  
     838  	block = add_zone(znr, corrected);
     839  	if (!block)
     840  		return;
     841  	read_block(block, blk);
     842  	for (i = 0; i < (MINIX_BLOCK_SIZE >> 1); i++)
     843  		add_zone(i + (unsigned short *)blk, &chg_blk);
     844  	if (chg_blk)
     845  		write_block(block, blk);
     846  }
     847  
     848  static void
     849  add_zone_ind2(unsigned int *znr, int *corrected) {
     850  	static char blk[MINIX_BLOCK_SIZE];
     851  	int i, chg_blk = 0;
     852  	int block;
     853  
     854  	block = add_zone2(znr, corrected);
     855  	if (!block)
     856  		return;
     857  	read_block(block, blk);
     858  	for (i = 0; i < MINIX_BLOCK_SIZE >> 2; i++)
     859  		add_zone2(i + (unsigned int *)blk, &chg_blk);
     860  	if (chg_blk)
     861  		write_block(block, blk);
     862  }
     863  
     864  static void
     865  add_zone_dind(unsigned short *znr, int *corrected) {
     866  	static char blk[MINIX_BLOCK_SIZE];
     867  	int i, blk_chg = 0;
     868  	int block;
     869  
     870  	block = add_zone(znr, corrected);
     871  	if (!block)
     872  		return;
     873  	read_block(block, blk);
     874  	for (i = 0; i < (MINIX_BLOCK_SIZE >> 1); i++)
     875  		add_zone_ind(i + (unsigned short *)blk, &blk_chg);
     876  	if (blk_chg)
     877  		write_block(block, blk);
     878  }
     879  
     880  static void
     881  add_zone_dind2(unsigned int *znr, int *corrected) {
     882  	static char blk[MINIX_BLOCK_SIZE];
     883  	int i, blk_chg = 0;
     884  	int block;
     885  
     886  	block = add_zone2(znr, corrected);
     887  	if (!block)
     888  		return;
     889  	read_block(block, blk);
     890  	for (i = 0; i < MINIX_BLOCK_SIZE >> 2; i++)
     891  		add_zone_ind2(i + (unsigned int *)blk, &blk_chg);
     892  	if (blk_chg)
     893  		write_block(block, blk);
     894  }
     895  
     896  static void
     897  add_zone_tind2(unsigned int *znr, int *corrected) {
     898  	static char blk[MINIX_BLOCK_SIZE];
     899  	int i, blk_chg = 0;
     900  	int block;
     901  
     902  	block = add_zone2(znr, corrected);
     903  	if (!block)
     904  		return;
     905  	read_block(block, blk);
     906  	for (i = 0; i < MINIX_BLOCK_SIZE >> 2; i++)
     907  		add_zone_dind2(i + (unsigned int *)blk, &blk_chg);
     908  	if (blk_chg)
     909  		write_block(block, blk);
     910  }
     911  
     912  static void
     913  check_zones(unsigned int i) {
     914  	struct minix_inode *inode;
     915  
     916  	if (!i || i > get_ninodes())
     917  		return;
     918  	if (inode_count[i] > 1)	/* have we counted this file already? */
     919  		return;
     920  	inode = Inode + i;
     921  	if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
     922  	    !S_ISLNK(inode->i_mode))
     923  		return;
     924  	for (i = 0; i < 7; i++)
     925  		add_zone(i + inode->i_zone, &changed);
     926  	add_zone_ind(7 + inode->i_zone, &changed);
     927  	add_zone_dind(8 + inode->i_zone, &changed);
     928  }
     929  
     930  static void
     931  check_zones2(unsigned int i) {
     932  	struct minix2_inode *inode;
     933  
     934  	if (!i || i > get_ninodes())
     935  		return;
     936  	if (inode_count[i] > 1)	/* have we counted this file already? */
     937  		return;
     938  	inode = Inode2 + i;
     939  	if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)
     940  	    && !S_ISLNK(inode->i_mode))
     941  		return;
     942  	for (i = 0; i < 7; i++)
     943  		add_zone2(i + inode->i_zone, &changed);
     944  	add_zone_ind2(7 + inode->i_zone, &changed);
     945  	add_zone_dind2(8 + inode->i_zone, &changed);
     946  	add_zone_tind2(9 + inode->i_zone, &changed);
     947  }
     948  
     949  static void
     950  check_file(struct minix_inode *dir, unsigned int offset) {
     951  	static char blk[MINIX_BLOCK_SIZE + 2];
     952  	struct minix_inode *inode;
     953  	unsigned int ino;
     954  	char *name;
     955  	int block;
     956  
     957  	block = map_block(dir, offset / MINIX_BLOCK_SIZE);
     958  	read_block(block, blk);
     959  	name = blk + (offset % MINIX_BLOCK_SIZE) + 2;
     960  	ino = *(unsigned short *)(name - 2);
     961  	if (ino > get_ninodes()) {
     962  		get_current_name();
     963  		printf(_("The directory '%s' contains a bad inode number "
     964  			 "for file '%.*s'."), current_name, (int)namelen, name);
     965  		if (ask(_(" Remove"), 1)) {
     966  			*(unsigned short *)(name - 2) = 0;
     967  			write_block(block, blk);
     968  		}
     969  		ino = 0;
     970  	}
     971  	if (name_depth < MAX_DEPTH)
     972  		xstrncpy(name_list[name_depth], name, namelen);
     973  	else
     974  		return;
     975  	name_depth++;
     976  	inode = get_inode(ino);
     977  	name_depth--;
     978  	if (!offset) {
     979  		if (!inode || strcmp(".", name) != 0) {
     980  			get_current_name();
     981  			printf(_("%s: bad directory: '.' isn't first\n"),
     982  			       current_name);
     983  			errors_uncorrected = 1;
     984  		} else
     985  			return;
     986  	}
     987  	if (offset == dirsize) {
     988  		if (!inode || strcmp("..", name) != 0) {
     989  			get_current_name();
     990  			printf(_("%s: bad directory: '..' isn't second\n"),
     991  			       current_name);
     992  			errors_uncorrected = 1;
     993  		} else
     994  			return;
     995  	}
     996  	if (!inode)
     997  		return;
     998  	if (name_depth < MAX_DEPTH)
     999  		xstrncpy(name_list[name_depth], name, namelen);
    1000  	else
    1001  		return;
    1002  	name_depth++;
    1003  	if (list) {
    1004  		if (verbose)
    1005  			printf("%6d %07o %3d ", ino,
    1006  			       inode->i_mode, inode->i_nlinks);
    1007  		get_current_name();
    1008  		printf("%s", current_name);
    1009  		if (S_ISDIR(inode->i_mode))
    1010  			printf(":\n");
    1011  		else
    1012  			printf("\n");
    1013  	}
    1014  	check_zones(ino);
    1015  	if (inode && S_ISDIR(inode->i_mode))
    1016  		recursive_check(ino);
    1017  	name_depth--;
    1018  }
    1019  
    1020  static void
    1021  check_file2(struct minix2_inode *dir, unsigned int offset) {
    1022  	static char blk[MINIX_BLOCK_SIZE + 4];
    1023  	struct minix2_inode *inode;
    1024  	ino_t ino;
    1025  	char *name;
    1026  	int block;
    1027  	const int version_offset = fs_version == 3 ? 4 : 2;
    1028  
    1029  	block = map_block2(dir, offset / MINIX_BLOCK_SIZE);
    1030  	read_block(block, blk);
    1031  	name = blk + (offset % MINIX_BLOCK_SIZE) + version_offset;
    1032  	ino = version_offset == 4 ? *(uint32_t *)(name - version_offset)
    1033  	                          : *(uint16_t *)(name - version_offset);
    1034  	if (ino > get_ninodes()) {
    1035  		get_current_name();
    1036  		printf(_("The directory '%s' contains a bad inode number "
    1037  			 "for file '%.*s'."), current_name, (int)namelen, name);
    1038  		if (ask(_(" Remove"), 1)) {
    1039  			memset(name - version_offset, 0, version_offset);
    1040  			write_block(block, blk);
    1041  		}
    1042  		ino = 0;
    1043  	}
    1044  	if (name_depth < MAX_DEPTH)
    1045  		xstrncpy(name_list[name_depth], name, namelen);
    1046  	else
    1047  		return;
    1048  	name_depth++;
    1049  	inode = get_inode2(ino);
    1050  	name_depth--;
    1051  	if (!offset) {
    1052  		if (!inode || strcmp(".", name) != 0) {
    1053  			get_current_name();
    1054  			printf(_("%s: bad directory: '.' isn't first\n"),
    1055  			       current_name);
    1056  			errors_uncorrected = 1;
    1057  		} else
    1058  			return;
    1059  	}
    1060  	if (offset == dirsize) {
    1061  		if (!inode || strcmp("..", name) != 0) {
    1062  			get_current_name();
    1063  			printf(_("%s: bad directory: '..' isn't second\n"),
    1064  			       current_name);
    1065  			errors_uncorrected = 1;
    1066  		} else
    1067  			return;
    1068  	}
    1069  	if (!inode)
    1070  		return;
    1071  	name_depth++;
    1072  	if (list) {
    1073  		if (verbose)
    1074  			printf("%6ju %07o %3d ", (uintmax_t)ino, inode->i_mode,
    1075  			       inode->i_nlinks);
    1076  		get_current_name();
    1077  		printf("%s", current_name);
    1078  		if (S_ISDIR(inode->i_mode))
    1079  			printf(":\n");
    1080  		else
    1081  			printf("\n");
    1082  	}
    1083  	check_zones2(ino);
    1084  	if (inode && S_ISDIR(inode->i_mode))
    1085  		recursive_check2(ino);
    1086  	name_depth--;
    1087  }
    1088  
    1089  static void
    1090  recursive_check(unsigned int ino) {
    1091  	struct minix_inode *dir;
    1092  	off_t offset;
    1093  
    1094  	dir = Inode + ino;
    1095  	if (!S_ISDIR(dir->i_mode))
    1096  		die(_("internal error"));
    1097  	if (dir->i_size < 2 * dirsize) {
    1098  		get_current_name();
    1099  		printf(_("%s: bad directory: size < 32"), current_name);
    1100  		errors_uncorrected = 1;
    1101  	}
    1102  
    1103  	if ((!repair || automatic) && !is_valid_zone_nr(*dir->i_zone)) {
    1104  		get_current_name();
    1105  		printf(_("%s: bad directory: invalid i_zone, use --repair to fix\n"), current_name);
    1106  		return;
    1107  	}
    1108  	for (offset = 0; offset < dir->i_size; offset += dirsize)
    1109  		check_file(dir, offset);
    1110  }
    1111  
    1112  static void
    1113  recursive_check2(unsigned int ino) {
    1114  	struct minix2_inode *dir;
    1115  	off_t offset;
    1116  
    1117  	dir = Inode2 + ino;
    1118  	if (!S_ISDIR(dir->i_mode))
    1119  		die(_("internal error"));
    1120  	if (dir->i_size < 2 * dirsize) {
    1121  		get_current_name();
    1122  		printf(_("%s: bad directory: size < 32"), current_name);
    1123  		errors_uncorrected = 1;
    1124  	}
    1125  	for (offset = 0; offset < dir->i_size; offset += dirsize)
    1126  		check_file2(dir, offset);
    1127  }
    1128  
    1129  static int
    1130  bad_zone(int i) {
    1131  	char buffer[1024];
    1132  
    1133  	if (MINIX_BLOCK_SIZE * i != lseek(device_fd, MINIX_BLOCK_SIZE * i, SEEK_SET))
    1134  		die(_("seek failed in bad_zone"));
    1135  	return (MINIX_BLOCK_SIZE != read(device_fd, buffer, MINIX_BLOCK_SIZE));
    1136  }
    1137  
    1138  static void
    1139  check_counts(void) {
    1140  	unsigned long i;
    1141  
    1142  	for (i = 1; i <= get_ninodes(); i++) {
    1143  		if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) {
    1144  			printf(_("Inode %lu mode not cleared."), i);
    1145  			if (ask(_("Clear"), 1)) {
    1146  				Inode[i].i_mode = 0;
    1147  				changed = 1;
    1148  			}
    1149  		}
    1150  		if (!inode_count[i]) {
    1151  			if (!inode_in_use(i))
    1152  				continue;
    1153  			printf(_("Inode %lu not used, marked 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 %lu used, marked unused in the bitmap."), i);
    1160  			if (ask(_("Set"), 1))
    1161  				mark_inode(i);
    1162  		}
    1163  		if (Inode[i].i_nlinks != inode_count[i]) {
    1164  			printf(_("Inode %lu (mode = %07o), i_nlinks=%d, counted=%d."),
    1165  			       i, Inode[i].i_mode, Inode[i].i_nlinks,
    1166  			       inode_count[i]);
    1167  			if (ask(_("Set i_nlinks to count"), 1)) {
    1168  				Inode[i].i_nlinks = inode_count[i];
    1169  				changed = 1;
    1170  			}
    1171  		}
    1172  	}
    1173  	for (i = get_first_zone(); i < get_nzones(); i++) {
    1174  		if (zone_in_use(i) == zone_count[i])
    1175  			continue;
    1176  		if (!zone_count[i]) {
    1177  			if (bad_zone(i))
    1178  				continue;
    1179  			printf(_("Zone %lu: marked in use, no file uses it."),
    1180  			       i);
    1181  			if (ask(_("Unmark"), 1))
    1182  				unmark_zone(i);
    1183  			continue;
    1184  		}
    1185  		if (zone_in_use(i))
    1186  			printf(_("Zone %lu: in use, counted=%d\n"),
    1187  			       i, zone_count[i]);
    1188  		else
    1189  			printf(_("Zone %lu: not in use, counted=%d\n"),
    1190  			       i, zone_count[i]);
    1191  	}
    1192  }
    1193  
    1194  static void
    1195  check_counts2(void) {
    1196  	unsigned long i;
    1197  
    1198  	for (i = 1; i <= get_ninodes(); i++) {
    1199  		if (!inode_in_use(i) && Inode2[i].i_mode && warn_mode) {
    1200  			printf(_("Inode %lu mode not cleared."), i);
    1201  			if (ask(_("Clear"), 1)) {
    1202  				Inode2[i].i_mode = 0;
    1203  				changed = 1;
    1204  			}
    1205  		}
    1206  		if (!inode_count[i]) {
    1207  			if (!inode_in_use(i))
    1208  				continue;
    1209  			printf(_("Inode %lu not used, marked used in the bitmap."), i);
    1210  			if (ask(_("Clear"), 1))
    1211  				unmark_inode(i);
    1212  			continue;
    1213  		}
    1214  		if (!inode_in_use(i)) {
    1215  			printf(_("Inode %lu used, marked unused in the bitmap."), i);
    1216  			if (ask(_("Set"), 1))
    1217  				mark_inode(i);
    1218  		}
    1219  		if (Inode2[i].i_nlinks != inode_count[i]) {
    1220  			printf(_("Inode %lu (mode = %07o), i_nlinks=%d, counted=%d."),
    1221  			       i, Inode2[i].i_mode, Inode2[i].i_nlinks,
    1222  			       inode_count[i]);
    1223  			if (ask(_("Set i_nlinks to count"), 1)) {
    1224  				Inode2[i].i_nlinks = inode_count[i];
    1225  				changed = 1;
    1226  			}
    1227  		}
    1228  	}
    1229  	for (i = get_first_zone(); i < get_nzones(); i++) {
    1230  		if (zone_in_use(i) == zone_count[i])
    1231  			continue;
    1232  		if (!zone_count[i]) {
    1233  			if (bad_zone(i))
    1234  				continue;
    1235  			printf(_("Zone %lu: marked in use, no file uses it."),
    1236  			       i);
    1237  			if (ask(_("Unmark"), 1))
    1238  				unmark_zone(i);
    1239  			continue;
    1240  		}
    1241  		if (zone_in_use(i))
    1242  			printf(_("Zone %lu: in use, counted=%d\n"),
    1243  			       i, zone_count[i]);
    1244  		else
    1245  			printf(_("Zone %lu: not in use, counted=%d\n"),
    1246  			       i, zone_count[i]);
    1247  	}
    1248  }
    1249  
    1250  static void
    1251  check(void) {
    1252  	memset(inode_count, 0, (get_ninodes() + 1) * sizeof(*inode_count));
    1253  	memset(zone_count, 0, get_nzones() * sizeof(*zone_count));
    1254  	check_zones(ROOT_INO);
    1255  	recursive_check(ROOT_INO);
    1256  	check_counts();
    1257  }
    1258  
    1259  static void
    1260  check2(void) {
    1261  	memset(inode_count, 0, (get_ninodes() + 1) * sizeof(*inode_count));
    1262  	memset(zone_count, 0, get_nzones() * sizeof(*zone_count));
    1263  	check_zones2(ROOT_INO);
    1264  	recursive_check2(ROOT_INO);
    1265  	check_counts2();
    1266  }
    1267  
    1268  int
    1269  main(int argc, char **argv) {
    1270  	struct termios tmp;
    1271  	int count;
    1272  	int retcode = FSCK_EX_OK;
    1273  	int i;
    1274  	static const struct option longopts[] = {
    1275  		{"list", no_argument, NULL, 'l'},
    1276  		{"auto", no_argument, NULL, 'a'},
    1277  		{"repair", no_argument, NULL, 'r'},
    1278  		{"verbose", no_argument, NULL, 'v'},
    1279  		{"super", no_argument, NULL, 's'},
    1280  		{"uncleared", no_argument, NULL, 'm'},
    1281  		{"force", no_argument, NULL, 'f'},
    1282  		{"version", no_argument, NULL, 'V'},
    1283  		{"help", no_argument, NULL, 'h'},
    1284  		{NULL, 0, NULL, 0}
    1285  	};
    1286  
    1287  	setlocale(LC_ALL, "");
    1288  	bindtextdomain(PACKAGE, LOCALEDIR);
    1289  	textdomain(PACKAGE);
    1290  	close_stdout_atexit();
    1291  
    1292  	strutils_set_exitcode(FSCK_EX_USAGE);
    1293  
    1294  	if (INODE_SIZE * MINIX_INODES_PER_BLOCK != MINIX_BLOCK_SIZE)
    1295  		die(_("bad inode size"));
    1296  	if (INODE2_SIZE * MINIX2_INODES_PER_BLOCK != MINIX_BLOCK_SIZE)
    1297  		die(_("bad v2 inode size"));
    1298  
    1299  	while ((i = getopt_long(argc, argv, "larvsmfVh", longopts, NULL)) != -1)
    1300  		switch (i) {
    1301  		case 'l':
    1302  			list = 1;
    1303  			break;
    1304  		case 'a':
    1305  			automatic = 1;
    1306  			repair = 1;
    1307  			break;
    1308  		case 'r':
    1309  			automatic = 0;
    1310  			repair = 1;
    1311  			break;
    1312  		case 'v':
    1313  			verbose = 1;
    1314  			break;
    1315  		case 's':
    1316  			show = 1;
    1317  			break;
    1318  		case 'm':
    1319  			warn_mode = 1;
    1320  			break;
    1321  		case 'f':
    1322  			force = 1;
    1323  			break;
    1324  		case 'V':
    1325  			print_version(FSCK_EX_OK);
    1326  		case 'h':
    1327  			usage();
    1328  		default:
    1329  			errtryhelp(FSCK_EX_USAGE);
    1330  		}
    1331  	argc -= optind;
    1332  	argv += optind;
    1333  	if (0 < argc) {
    1334  		device_name = argv[0];
    1335  	} else {
    1336  		warnx(_("no device specified"));
    1337  		errtryhelp(FSCK_EX_USAGE);
    1338  	}
    1339  	check_mount();		/* trying to check a mounted filesystem? */
    1340  	if (repair && !automatic && (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)))
    1341  		die(_("need terminal for interactive repairs"));
    1342  
    1343  	device_fd = open(device_name, repair ? O_RDWR : O_RDONLY);
    1344  	if (device_fd < 0)
    1345  		die(_("cannot open %s: %s"), device_name, strerror(errno));
    1346  	for (count = 0; count < 3; count++)
    1347  		sync();
    1348  	read_superblock();
    1349  
    1350  	/* Determine whether or not we should continue with the checking.  This
    1351  	 * is based on the status of the filesystem valid and error flags and
    1352  	 * whether or not the -f switch was specified on the command line.  */
    1353  	if (fs_version < 3 && !(Super.s_state & MINIX_ERROR_FS) &&
    1354  	    (Super.s_state & MINIX_VALID_FS) && !force) {
    1355  		if (repair)
    1356  			printf(_("%s is clean, no check.\n"), device_name);
    1357  		return retcode;
    1358  	}
    1359  
    1360  	if (force)
    1361  		printf(_("Forcing filesystem check on %s.\n"), device_name);
    1362  	else if (repair)
    1363  		printf(_("Filesystem on %s is dirty, needs checking.\n"),
    1364  		       device_name);
    1365  
    1366  	read_tables();
    1367  
    1368  	/* Restore the terminal state on fatal signals.  We don't do this for
    1369  	 * SIGALRM, SIGUSR1 or SIGUSR2.  */
    1370  	signal(SIGINT, fatalsig);
    1371  	signal(SIGQUIT, fatalsig);
    1372  	signal(SIGTERM, fatalsig);
    1373  
    1374  	if (repair && !automatic) {
    1375  		tcgetattr(STDIN_FILENO, &termios);
    1376  		tmp = termios;
    1377  		tmp.c_lflag &= ~(ICANON | ECHO);
    1378  		tcsetattr(STDIN_FILENO, TCSANOW, &tmp);
    1379  		termios_set = 1;
    1380  	}
    1381  
    1382  	if (fs_version == 2 || fs_version == 3) {
    1383  		check_root2();
    1384  		check2();
    1385  	} else {
    1386  		check_root();
    1387  		check();
    1388  	}
    1389  	if (verbose) {
    1390  		unsigned long inode, free;
    1391  
    1392  		for (inode = 1, free = 0; inode <= get_ninodes(); inode++)
    1393  			if (!inode_in_use(inode))
    1394  				free++;
    1395  		printf(_("\n%6ld inodes used (%ld%%)\n"),
    1396  		       (get_ninodes() - free),
    1397  		       100 * (get_ninodes() - free) / get_ninodes());
    1398  		for (inode = get_first_zone(), free = 0; inode < get_nzones(); inode++)
    1399  			if (!zone_in_use(inode))
    1400  				free++;
    1401  		printf(_("%6ld zones used (%ld%%)\n"), (get_nzones() - free),
    1402  		       100 * (get_nzones() - free) / get_nzones());
    1403  		printf(_("\n%6d regular files\n"
    1404  			 "%6d directories\n"
    1405  			 "%6d character device files\n"
    1406  			 "%6d block device files\n"
    1407  			 "%6d links\n"
    1408  			 "%6d symbolic links\n"
    1409  			 "------\n"
    1410  			 "%6d files\n"),
    1411  		       regular, directory, chardev, blockdev,
    1412  		       links - 2 * directory + 1, symlinks,
    1413  		       total - 2 * directory + 1);
    1414  	}
    1415  	if (changed) {
    1416  		write_tables();
    1417  		printf(_("----------------------------\n"
    1418  			 "FILE SYSTEM HAS BEEN CHANGED\n"
    1419  			 "----------------------------\n"));
    1420  		for (count = 0; count < 3; count++)
    1421  			sync();
    1422  	} else if (repair)
    1423  		write_super_block();
    1424  
    1425  	if (repair && !automatic)
    1426  		tcsetattr(STDIN_FILENO, TCSANOW, &termios);
    1427  
    1428  	if (close_fd(device_fd) != 0)
    1429  		err(FSCK_EX_ERROR, _("write failed"));
    1430  	if (changed)
    1431  		retcode += 3;
    1432  	if (errors_uncorrected)
    1433  		retcode += 4;
    1434  	return retcode;
    1435  }