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