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