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