Update web page...
[oweals/busybox.git] / 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 <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;
103 typedef unsigned short u16;
104 typedef unsigned int u32;
105
106
107 static const int MINIX_ROOT_INO = 1;
108 static const int MINIX_LINK_MAX = 250;
109 static const int MINIX2_LINK_MAX = 65530;
110
111 static const int MINIX_I_MAP_SLOTS = 8;
112 static const int MINIX_Z_MAP_SLOTS = 64;
113 static const int MINIX_SUPER_MAGIC = 0x137F;            /* original minix fs */
114 static const int MINIX_SUPER_MAGIC2 = 0x138F;           /* minix fs, 30 char names */
115 static const int MINIX2_SUPER_MAGIC = 0x2468;           /* minix V2 fs */
116 static const int MINIX2_SUPER_MAGIC2 = 0x2478;          /* minix V2 fs, 30 char names */
117 static const int MINIX_VALID_FS = 0x0001;               /* Clean fs. */
118 static 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
123 static const int MINIX_V1 = 0x0001;             /* original minix fs */
124 static 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  */
132 struct 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  */
148 struct 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  */
163 struct 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
176 struct 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
196 static 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
211 static char *program_version = "1.2 - 11/11/96";
212 static char *device_name = NULL;
213 static int IN;
214 static int repair = 0, automatic = 0, verbose = 0, list = 0, show =
215         0, warn_mode = 0, force = 0;
216 static int directory = 0, regular = 0, blockdev = 0, chardev = 0, links =
217         0, symlinks = 0, total = 0;
218
219 static int changed = 0;                 /* flags if the filesystem has been changed */
220 static int errors_uncorrected = 0;      /* flag if some error was not corrected */
221 static int dirsize = 16;
222 static int namelen = 14;
223 static int version2 = 0;
224 static struct termios termios;
225 static int termios_set = 0;
226
227 /* File-name data */
228 static const int MAX_DEPTH = 32;
229 static int name_depth = 0;
230 // static char name_list[MAX_DEPTH][BUFSIZ + 1];
231 static char **name_list = NULL;
232
233 static char *inode_buffer = NULL;
234
235 #define Inode (((struct minix_inode *) inode_buffer)-1)
236 #define Inode2 (((struct minix2_inode *) inode_buffer)-1)
237 static 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
254 static char *inode_map;
255 static char *zone_map;
256
257 static unsigned char *inode_count = NULL;
258 static unsigned char *zone_count = NULL;
259
260 static void recursive_check(unsigned int ino);
261 #ifdef BB_FEATURE_MINIX2
262 static void recursive_check2(unsigned int ino);
263 #endif
264
265 static 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
278 static void leave(int) __attribute__ ((noreturn));
279 static void leave(int status)
280 {
281         if (termios_set)
282                 tcsetattr(0, TCSANOW, &termios);
283         exit(status);
284 }
285
286 static 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  */
296 static 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
306 static 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  */
353 static 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  */
398 static 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
418 static 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  */
441 static 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  */
465 static 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  */
490 static 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
525 static 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
583 static 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
604 static 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
616 static 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
639 static 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
673 static 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
704 static 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
752 static 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
799 static 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
808 static 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
817 static 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
851 static 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
885 static 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
902 static 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
919 static 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
936 static 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
952 static 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
969 static 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
987 static 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
1007 static 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
1072 static 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
1135 static 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
1153 static 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
1171 static 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
1180 static 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
1231 static 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
1283 static 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
1293 static 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) */
1305 static 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 */
1317 static 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
1332 extern 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 }