home *** CD-ROM | disk | FTP | other *** search
/ Chip 1995 March / CHIP3.mdf / slackwar / a / util / util-lin.2 / util-lin / util-linux-2.2 / disk-utils / fsck.minix.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-22  |  21.5 KB  |  863 lines

  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.  * 10.12.94  -  added test to prevent checking of mounted fs adapted
  42.  *              from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
  43.  *              program.  (Daniel Quinlan, quinlan@yggdrasil.com)
  44.  *
  45.  * I've had no time to add comments - hopefully the function names
  46.  * are comments enough. As with all file system checkers, this assumes
  47.  * the file system is quiescent - don't use it on a mounted device
  48.  * unless you can be sure nobody is writing to it (and remember that the
  49.  * kernel can write to it when it searches for files).
  50.  *
  51.  * Usuage: fsck [-larvsm] device
  52.  *    -l for a listing of all the filenames
  53.  *    -a for automatic repairs (not implemented)
  54.  *    -r for repairs (interactive) (not implemented)
  55.  *    -v for verbose (tells how many files)
  56.  *    -s for super-block info
  57.  *    -m for minix-like "mode not cleared" warnings
  58.  *    -f force filesystem check even if filesystem marked as valid
  59.  *
  60.  * The device may be a block device or a image of one, but this isn't
  61.  * enforced (but it's not much fun on a character device :-). 
  62.  */
  63.  
  64. #include <stdio.h>
  65. #include <unistd.h>
  66. #include <string.h>
  67. #include <fcntl.h>
  68. #include <ctype.h>
  69. #include <stdlib.h>
  70. #include <termios.h>
  71. #include <mntent.h>
  72. #include <sys/stat.h>
  73.  
  74. #include <linux/fs.h>
  75. #include <linux/minix_fs.h>
  76.  
  77. #ifndef __GNUC__
  78. #error "needs gcc for the bitop-__asm__'s"
  79. #endif
  80.  
  81. #ifndef __linux__
  82. #define volatile
  83. #endif
  84.  
  85. #define ROOT_INO 1
  86.  
  87. #define UPPER(size,n) ((size+((n)-1))/(n))
  88. #define INODE_SIZE (sizeof(struct minix_inode))
  89. #define INODE_BLOCKS UPPER(INODES,MINIX_INODES_PER_BLOCK)
  90. #define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
  91.  
  92. #define BITS_PER_BLOCK (BLOCK_SIZE<<3)
  93.  
  94. static char * program_name = "fsck.minix";
  95. static char * program_version = "1.0 - 12/30/93";
  96. static char * device_name = NULL;
  97. static int IN;
  98. static int repair=0, automatic=0, verbose=0, list=0, show=0, warn_mode=0, 
  99.     force=0;
  100. static int directory=0, regular=0, blockdev=0, chardev=0, links=0,
  101.         symlinks=0, total=0;
  102.  
  103. static int changed = 0; /* flags if the filesystem has been changed */
  104. static int errors_uncorrected = 0; /* flag if some error was not corrected */
  105. static int dirsize = 16;
  106. static int namelen = 14;
  107.  
  108. /* File-name data */
  109. #define MAX_DEPTH 50
  110. static int name_depth = 0;
  111. static char name_list[MAX_DEPTH][NAME_MAX+1];
  112.  
  113. static char * inode_buffer = NULL;
  114. #define Inode (((struct minix_inode *) inode_buffer)-1)
  115. static char super_block_buffer[BLOCK_SIZE];
  116. #define Super (*(struct minix_super_block *)super_block_buffer)
  117. #define INODES ((unsigned long)Super.s_ninodes)
  118. #define ZONES ((unsigned long)Super.s_nzones)
  119. #define IMAPS ((unsigned long)Super.s_imap_blocks)
  120. #define ZMAPS ((unsigned long)Super.s_zmap_blocks)
  121. #define FIRSTZONE ((unsigned long)Super.s_firstdatazone)
  122. #define ZONESIZE ((unsigned long)Super.s_log_zone_size)
  123. #define MAXSIZE ((unsigned long)Super.s_max_size)
  124. #define MAGIC (Super.s_magic)
  125. #define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
  126.  
  127. static char inode_map[BLOCK_SIZE * MINIX_I_MAP_SLOTS];
  128. static char zone_map[BLOCK_SIZE * MINIX_Z_MAP_SLOTS];
  129.  
  130. static unsigned char * inode_count = NULL;
  131. static unsigned char * zone_count = NULL;
  132.  
  133. void recursive_check(unsigned int ino);
  134.  
  135. #define bitop(name,op) \
  136. static inline int name(char * addr,unsigned int nr) \
  137. { \
  138. int __res; \
  139. __asm__ __volatile__("bt" op "l %1,%2; adcl $0,%0" \
  140. :"=g" (__res) \
  141. :"r" (nr),"m" (*(addr)),"0" (0)); \
  142. return __res; \
  143. }
  144.  
  145. bitop(bit,"")
  146. bitop(setbit,"s")
  147. bitop(clrbit,"r")
  148.  
  149. #define inode_in_use(x) (bit(inode_map,(x)))
  150. #define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
  151.  
  152. #define mark_inode(x) (setbit(inode_map,(x)),changed=1)
  153. #define unmark_inode(x) (clrbit(inode_map,(x)),changed=1)
  154.  
  155. #define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1)
  156. #define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1)
  157.  
  158. /*
  159.  * Volatile to let gcc know that this doesn't return. When trying
  160.  * to compile this under minix, volatile gives a warning, as
  161.  * exit() isn't defined as volatile under minix.
  162.  */
  163. volatile void fatal_error(const char * fmt_string, int status)
  164. {
  165.     fprintf(stderr,fmt_string,program_name,device_name);
  166.     exit(status);
  167. }
  168.  
  169. #define usage() fatal_error("Usage: %s [-larvsmf] /dev/name\n",16)
  170. #define die(str) fatal_error("%s: " str "\n",8)
  171.  
  172. /*
  173.  * This simply goes through the file-name data and prints out the
  174.  * current file.
  175.  */
  176. void print_current_name(void)
  177. {
  178.     int i=0;
  179.  
  180.     while (i<name_depth)
  181.         printf("/%.*s",namelen,name_list[i++]);
  182. }
  183.  
  184. int ask(const char * string,int def)
  185. {
  186.     int c;
  187.  
  188.     if (!repair) {
  189.         printf("\n");
  190.         errors_uncorrected = 1;
  191.         return 0;
  192.     }
  193.     if (automatic) {
  194.         printf("\n");
  195.         if (!def)
  196.               errors_uncorrected = 1;
  197.         return def;
  198.     }
  199.     printf(def?"%s (y/n)? ":"%s (n/y)? ",string);
  200.     for (;;) {
  201.         fflush(stdout);
  202.         if ((c=getchar())==EOF) {
  203.                 if (!def)
  204.                   errors_uncorrected = 1;
  205.             return def;
  206.         }
  207.         c=toupper(c);
  208.         if (c == 'Y') {
  209.             def = 1;
  210.             break;
  211.         } else if (c == 'N') {
  212.             def = 0;
  213.             break;
  214.         } else if (c == ' ' || c == '\n')
  215.             break;
  216.     }
  217.     if (def)
  218.         printf("y\n");
  219.     else {
  220.         printf("n\n");
  221.         errors_uncorrected = 1;
  222.          }
  223.     return def;
  224. }
  225.  
  226. /*
  227.  * Make certain that we aren't checking a filesystem that is on a
  228.  * mounted partition.  Code adapted from e2fsck, Copyright (C) 1993,
  229.  * 1994 Theodore Ts'o.  Also licensed under GPL.
  230.  */
  231. static void check_mount(void)
  232. {
  233.     FILE * f;
  234.     struct mntent * mnt;
  235.     int cont;
  236.     int fd;
  237.  
  238.     if ((f = setmntent (MOUNTED, "r")) == NULL)
  239.         return;
  240.     while ((mnt = getmntent (f)) != NULL)
  241.         if (strcmp (device_name, mnt->mnt_fsname) == 0)
  242.             break;
  243.     endmntent (f);
  244.     if (!mnt)
  245.         return;
  246.  
  247.     /*
  248.      * If the root is mounted read-only, then /etc/mtab is
  249.      * probably not correct; so we won't issue a warning based on
  250.      * it.
  251.      */
  252.     fd = open(MOUNTED, O_RDWR);
  253.     if (fd < 0 && errno == EROFS)
  254.         return;
  255.     else
  256.         close(fd);
  257.     
  258.     printf ("%s is mounted.     ", device_name);
  259.     if (isatty(0) && isatty(1))
  260.         cont = ask("Do you really want to continue", 0);
  261.     else
  262.         cont = 0;
  263.     if (!cont) {
  264.         printf ("check aborted.\n");
  265.         exit (0);
  266.     }
  267.     return;
  268. }
  269.  
  270. /*
  271.  * check_zone_nr checks to see that *nr is a valid zone nr. If it
  272.  * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
  273.  * if an error was corrected, and returns the zone (0 for no zone
  274.  * or a bad zone-number).
  275.  */
  276. int check_zone_nr(unsigned short * nr, int * corrected)
  277. {
  278.     if (!*nr)
  279.         return 0;
  280.     if (*nr < FIRSTZONE)
  281.         printf("Zone nr < FIRSTZONE in file `");
  282.     else if (*nr >= ZONES)
  283.         printf("Zone nr >= ZONES in file `");
  284.     else
  285.         return *nr;
  286.     print_current_name();
  287.     printf("'.");
  288.     if (ask("Remove block",1)) {
  289.         *nr = 0;
  290.         *corrected = 1;
  291.     }
  292.     return 0;
  293. }
  294.  
  295. /*
  296.  * read-block reads block nr into the buffer at addr.
  297.  */
  298. void read_block(unsigned int nr, char * addr)
  299. {
  300.     if (!nr) {
  301.         memset(addr,0,BLOCK_SIZE);
  302.         return;
  303.     }
  304.     if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET)) {
  305.         printf("Read error: unable to seek to block in file '");
  306.         print_current_name();
  307.         printf("'\n");
  308.         memset(addr,0,BLOCK_SIZE);
  309.         errors_uncorrected = 1;
  310.     } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) {
  311.         printf("Read error: bad block in file '");
  312.         print_current_name();
  313.         printf("'\n");
  314.         memset(addr,0,BLOCK_SIZE);
  315.         errors_uncorrected = 1;
  316.     }
  317. }
  318.  
  319. /*
  320.  * write_block writes block nr to disk.
  321.  */
  322. void write_block(unsigned int nr, char * addr)
  323. {
  324.     if (!nr)
  325.         return;
  326.     if (nr < FIRSTZONE || nr >= ZONES) {
  327.         printf("Internal error: trying to write bad block\n"
  328.         "Write request ignored\n");
  329.         errors_uncorrected = 1;
  330.         return;
  331.     }
  332.     if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET))
  333.         die("seek failed in write_block");
  334.     if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) {
  335.         printf("Write error: bad block in file '");
  336.         print_current_name();
  337.         printf("'\n");
  338.         errors_uncorrected = 1;
  339.     }
  340. }
  341.  
  342. /*
  343.  * map-block calculates the absolute block nr of a block in a file.
  344.  * It sets 'changed' if the inode has needed changing, and re-writes
  345.  * any indirect blocks with errors.
  346.  */
  347. int map_block(struct minix_inode * inode, unsigned int blknr)
  348. {
  349.     unsigned short ind[BLOCK_SIZE>>1];
  350.     unsigned short dind[BLOCK_SIZE>>1];
  351.     int blk_chg, block, result;
  352.  
  353.     if (blknr<7)
  354.         return check_zone_nr(inode->i_zone + blknr, &changed);
  355.     blknr -= 7;
  356.     if (blknr<512) {
  357.         block = check_zone_nr(inode->i_zone + 7, &changed);
  358.         read_block(block, (char *) ind);
  359.         blk_chg = 0;
  360.         result = check_zone_nr(blknr + ind, &blk_chg);
  361.         if (blk_chg)
  362.             write_block(block, (char *) ind);
  363.         return result;
  364.     }
  365.     blknr -= 512;
  366.     block = check_zone_nr(inode->i_zone + 8, &changed);
  367.     read_block(block, (char *) dind);
  368.     blk_chg = 0;
  369.     result = check_zone_nr(dind + (blknr/512), &blk_chg);
  370.     if (blk_chg)
  371.         write_block(block, (char *) dind);
  372.     block = result;
  373.     read_block(block, (char *) ind);
  374.     blk_chg = 0;
  375.     result = check_zone_nr(ind + (blknr%512), &blk_chg);
  376.     if (blk_chg)
  377.         write_block(block, (char *) ind);
  378.     return result;
  379. }
  380.  
  381. void write_super_block(void)
  382. {
  383.     /*
  384.      * Set the state of the filesystem based on whether or not there
  385.      * are uncorrected errors.  The filesystem valid flag is
  386.      * unconditionally set if we get this far.
  387.      */
  388.     Super.s_state |= MINIX_VALID_FS;
  389.     if ( errors_uncorrected )
  390.         Super.s_state |= MINIX_ERROR_FS;
  391.     else
  392.         Super.s_state &= ~MINIX_ERROR_FS;
  393.     
  394.     if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
  395.         die("seek failed in write_super_block");
  396.     if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE))
  397.         die("unable to write super-block");
  398.  
  399.     return;
  400. }
  401.  
  402. void write_tables(void)
  403. {
  404.     write_super_block();
  405.  
  406.     if (IMAPS*BLOCK_SIZE != write(IN,inode_map,IMAPS*BLOCK_SIZE))
  407.         die("Unable to write inode map");
  408.     if (ZMAPS*BLOCK_SIZE != write(IN,zone_map,ZMAPS*BLOCK_SIZE))
  409.         die("Unable to write zone map");
  410.     if (INODE_BUFFER_SIZE != write(IN,inode_buffer,INODE_BUFFER_SIZE))
  411.         die("Unable to write inodes");
  412. }
  413.  
  414. void read_tables(void)
  415. {
  416.     memset(inode_map,0,sizeof(inode_map));
  417.     memset(zone_map,0,sizeof(zone_map));
  418.     if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
  419.         die("seek failed");
  420.     if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE))
  421.         die("unable to read super block");
  422.     if (MAGIC == MINIX_SUPER_MAGIC) {
  423.         namelen = 14;
  424.         dirsize = 16;
  425.     } else if (MAGIC == MINIX_SUPER_MAGIC2) {
  426.         namelen = 30;
  427.         dirsize = 32;
  428.     } else
  429.         die("bad magic number in super-block");
  430.     if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
  431.         die("Only 1k blocks/zones supported");
  432.     if (!IMAPS || IMAPS > MINIX_I_MAP_SLOTS)
  433.         die("bad s_imap_blocks field in super-block");
  434.     if (!ZMAPS || ZMAPS > MINIX_Z_MAP_SLOTS)
  435.         die("bad s_zmap_blocks field in super-block");
  436.     inode_buffer = malloc(INODE_BUFFER_SIZE);
  437.     if (!inode_buffer)
  438.         die("Unable to allocate buffer for inodes");
  439.     inode_count = malloc(INODES);
  440.     if (!inode_count)
  441.         die("Unable to allocate buffer for inode count");
  442.     zone_count = malloc(ZONES);
  443.     if (!zone_count)
  444.         die("Unable to allocate buffer for zone count");
  445.     if (IMAPS*BLOCK_SIZE != read(IN,inode_map,IMAPS*BLOCK_SIZE))
  446.         die("Unable to read inode map");
  447.     if (ZMAPS*BLOCK_SIZE != read(IN,zone_map,ZMAPS*BLOCK_SIZE))
  448.         die("Unable to read zone map");
  449.     if (INODE_BUFFER_SIZE != read(IN,inode_buffer,INODE_BUFFER_SIZE))
  450.         die("Unable to read inodes");
  451.     if (NORM_FIRSTZONE != FIRSTZONE) {
  452.         printf("Warning: Firstzone != Norm_firstzone\n");
  453.         errors_uncorrected = 1;
  454.     }
  455.     if (show) {
  456.         printf("%d inodes\n",INODES);
  457.         printf("%d blocks\n",ZONES);
  458.         printf("Firstdatazone=%d (%d)\n",FIRSTZONE,NORM_FIRSTZONE);
  459.         printf("Zonesize=%d\n",BLOCK_SIZE<<ZONESIZE);
  460.         printf("Maxsize=%d\n",MAXSIZE);
  461.         printf("Filesystem state=%d\n", Super.s_state);
  462.         printf("namelen=%d\n\n",namelen);
  463.     }
  464. }
  465.  
  466. struct minix_inode * get_inode(unsigned int nr)
  467. {
  468.     struct minix_inode * inode;
  469.  
  470.     if (!nr || nr >= INODES)
  471.         return NULL;
  472.     total++;
  473.     inode = Inode + nr;
  474.     if (!inode_count[nr]) {
  475.         if (!inode_in_use(nr)) {
  476.             printf("Inode %d marked not used, but used for file '",
  477.                 nr);
  478.             print_current_name();
  479.             printf("'\n");
  480.             if (repair)
  481.                 if (ask("Mark in use",1))
  482.                     mark_inode(nr);
  483.             else
  484.                     errors_uncorrected = 1;
  485.         }
  486.         if (S_ISDIR(inode->i_mode))
  487.             directory++;
  488.         else if (S_ISREG(inode->i_mode))
  489.             regular++;
  490.         else if (S_ISCHR(inode->i_mode))
  491.             chardev++;
  492.         else if (S_ISBLK(inode->i_mode))
  493.             blockdev++;
  494.         else if (S_ISLNK(inode->i_mode))
  495.             symlinks++;
  496.         else if (S_ISSOCK(inode->i_mode))
  497.             ;
  498.         else if (S_ISFIFO(inode->i_mode))
  499.             ;
  500.         else {
  501.                         print_current_name();
  502.                         printf(" has mode %05o\n",inode->i_mode);
  503.                 }
  504.  
  505.     } else
  506.         links++;
  507.     if (!++inode_count[nr]) {
  508.         printf("Warning: inode count too big.\n");
  509.         inode_count[nr]--;
  510.         errors_uncorrected = 1;
  511.     }
  512.     return inode;
  513. }
  514.  
  515. void check_root(void)
  516. {
  517.     struct minix_inode * inode = Inode + ROOT_INO;
  518.  
  519.     if (!inode || !S_ISDIR(inode->i_mode))
  520.         die("root inode isn't a directory");
  521. }
  522.  
  523. static int add_zone(unsigned short * znr, int * corrected)
  524. {
  525.     int result;
  526.     int block;
  527.  
  528.     result = 0;
  529.     block = check_zone_nr(znr, corrected);
  530.     if (!block)
  531.         return 0;
  532.     if (zone_count[block]) {
  533.         printf("Block has been used before. Now in file `");
  534.         print_current_name();
  535.         printf("'.");
  536.         if (ask("Clear",1)) {
  537.             *znr = 0;
  538.             block = 0;
  539.             *corrected = 1;
  540.         }
  541.     }
  542.     if (!block)
  543.         return 0;
  544.     if (!zone_in_use(block)) {
  545.         printf("Block %d in file `",block);
  546.         print_current_name();
  547.         printf("' is marked not in use.");
  548.         if (ask("Correct",1))
  549.             mark_zone(block);
  550.     }
  551.     if (!++zone_count[block])
  552.         zone_count[block]--;
  553.     return block;
  554. }
  555.  
  556. static void add_zone_ind(unsigned short * znr, int * corrected)
  557. {
  558.     static char blk[BLOCK_SIZE];
  559.     int i, chg_blk=0;
  560.     int block;
  561.  
  562.     block = add_zone(znr, corrected);
  563.     if (!block)
  564.         return;
  565.     read_block(block, blk);
  566.     for (i=0 ; i < (BLOCK_SIZE>>1) ; i++)
  567.         add_zone(i + (unsigned short *) blk, &chg_blk);
  568.     if (chg_blk)
  569.         write_block(block, blk);
  570. }
  571.  
  572. static void add_zone_dind(unsigned short * znr, int * corrected)
  573. {
  574.     static char blk[BLOCK_SIZE];
  575.     int i, blk_chg=0;
  576.     int block;
  577.  
  578.     block = add_zone(znr, corrected);
  579.     if (!block)
  580.         return;
  581.     read_block(block, blk);
  582.     for (i=0 ; i < (BLOCK_SIZE>>1) ; i++)
  583.         add_zone_ind(i + (unsigned short *) blk, &blk_chg);
  584.     if (blk_chg)
  585.         write_block(block, blk);
  586. }
  587.  
  588. void check_zones(unsigned int i)
  589. {
  590.     struct minix_inode * inode;
  591.  
  592.     if (!i || i >= INODES)
  593.         return;
  594.     if (inode_count[i] > 1)    /* have we counted this file already? */
  595.         return;
  596.     inode = Inode + i;
  597.     if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
  598.         !S_ISLNK(inode->i_mode))
  599.         return;
  600.     for (i=0 ; i<7 ; i++)
  601.         add_zone(i + inode->i_zone, &changed);
  602.     add_zone_ind(7 + inode->i_zone, &changed);
  603.     add_zone_dind(8 + inode->i_zone, &changed);
  604. }
  605.  
  606. void check_file(struct minix_inode * dir, unsigned int offset)
  607. {
  608.     static char blk[BLOCK_SIZE];
  609.     struct minix_inode * inode;
  610.     int ino;
  611.     char * name;
  612.     int block;
  613.  
  614.     block = map_block(dir,offset/BLOCK_SIZE);
  615.     read_block(block, blk);
  616.     name = blk + (offset % BLOCK_SIZE) + 2;
  617.     ino = * (unsigned short *) (name-2);
  618.     if (ino >= INODES) {
  619.         print_current_name();
  620.         printf(" contains a bad inode number for file '");
  621.         printf("%.*s'.",namelen,name);
  622.         if (ask(" Remove",1)) {
  623.             *(unsigned short *)(name-2) = 0;
  624.             write_block(block, blk);
  625.         }
  626.         ino = 0;
  627.     }    
  628.     inode = get_inode(ino);
  629.     if (!offset)
  630.         if (!inode || strcmp(".",name)) {
  631.             print_current_name();
  632.             printf(": bad directory: '.' isn't first\n");
  633.             errors_uncorrected = 1;
  634.         } else return;
  635.     if (offset == dirsize)
  636.         if (!inode || strcmp("..",name)) {
  637.             print_current_name();
  638.             printf(": bad directory: '..' isn't second\n");
  639.             errors_uncorrected = 1;
  640.         } else return;
  641.     if (!inode)
  642.         return;
  643.     if (name_depth < MAX_DEPTH)
  644.         strncpy(name_list[name_depth],name,namelen);
  645.     name_depth++;    
  646.     if (list) {
  647.         if (verbose)
  648.             printf("%6d %07o %3d ",ino,inode->i_mode,inode->i_nlinks);
  649.         print_current_name();
  650.         if (S_ISDIR(inode->i_mode))
  651.             printf(":\n");
  652.         else
  653.             printf("\n");
  654.     }
  655.     check_zones(ino);
  656.     if (inode && S_ISDIR(inode->i_mode))
  657.         recursive_check(ino);
  658.     name_depth--;
  659.     return;
  660. }
  661.  
  662. void recursive_check(unsigned int ino)
  663. {
  664.     struct minix_inode * dir;
  665.     unsigned int offset;
  666.  
  667.     dir = Inode + ino;
  668.     if (!S_ISDIR(dir->i_mode))
  669.         die("internal error");
  670.     if (dir->i_size < 32) {
  671.         print_current_name();
  672.         printf(": bad directory: size<32");
  673.         errors_uncorrected = 1;
  674.     }
  675.     for (offset = 0 ; offset < dir->i_size ; offset += dirsize)
  676.         check_file(dir,offset);
  677. }
  678.  
  679. int bad_zone(int i)
  680. {
  681.     char buffer[1024];
  682.  
  683.     if (BLOCK_SIZE*i != lseek(IN, BLOCK_SIZE*i, SEEK_SET))
  684.         die("seek failed in bad_zone");
  685.     return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE));
  686. }
  687.  
  688. void check_counts(void)
  689. {
  690.     int i;
  691.  
  692.     for (i=1 ; i < INODES ; i++) {
  693.         if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) {
  694.             printf("Inode %d mode not cleared.",i);
  695.             if (ask("Clear",1)) {
  696.                 Inode[i].i_mode = 0;
  697.                 changed = 1;
  698.             }
  699.         }
  700.         if (!inode_count[i]) {
  701.             if (!inode_in_use(i))
  702.                 continue;
  703.             printf("Inode %d not used, marked used in the bitmap.",i);
  704.             if (ask("Clear",1))
  705.                 unmark_inode(i);
  706.             continue;
  707.         }
  708.         if (!inode_in_use(i)) {
  709.             printf("Inode %d used, marked unused in the bitmap.",
  710.                 i);
  711.             if (ask("Set",1))
  712.                 mark_inode(i);
  713.         }
  714.         if (Inode[i].i_nlinks != inode_count[i]) {
  715.             printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.",
  716.                 i,Inode[i].i_mode,Inode[i].i_nlinks,inode_count[i]);
  717.             if (ask("Set i_nlinks to count",1)) {
  718.                 Inode[i].i_nlinks=inode_count[i];
  719.                 changed=1;
  720.             }
  721.         }
  722.     }
  723.     for (i=FIRSTZONE ; i < ZONES ; i++) {
  724.         if (zone_in_use(i) == zone_count[i])
  725.             continue;
  726.         if (!zone_count[i]) {
  727.             if (bad_zone(i))
  728.                 continue;
  729.             printf("Zone %d: marked in use, no file uses it.",i);
  730.             if (ask("Unmark",1))
  731.                 unmark_zone(i);
  732.             continue;
  733.         }
  734.         printf("Zone %d: %sin use, counted=%d\n",
  735.         i,zone_in_use(i)?"":"not ",zone_count[i]);
  736.     }
  737. }
  738.  
  739. void check(void)
  740. {
  741.     memset(inode_count,0,INODES*sizeof(*inode_count));
  742.     memset(zone_count,0,ZONES*sizeof(*zone_count));
  743.     check_zones(ROOT_INO);
  744.     recursive_check(ROOT_INO);
  745.     check_counts();
  746. }
  747.  
  748. int main(int argc, char ** argv)
  749. {
  750.     struct termios termios,tmp;
  751.     int count;
  752.     int retcode = 0;
  753.  
  754.     if (argc && *argv)
  755.         program_name = *argv;
  756.     if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
  757.         die("bad inode size");
  758.     while (argc-- > 1) {
  759.         argv++;
  760.         if (argv[0][0] != '-')
  761.             if (device_name)
  762.                 usage();
  763.             else
  764.                 device_name = argv[0];
  765.         else while (*++argv[0])
  766.             switch (argv[0][0]) {
  767.                 case 'l': list=1; break;
  768.                 case 'a': automatic=1; repair=1; break;
  769.                 case 'r': automatic=0; repair=1; break;
  770.                 case 'v': verbose=1; break;
  771.                 case 's': show=1; break;
  772.                 case 'm': warn_mode=1; break;
  773.                 case 'f': force=1; break;
  774.                 default: usage();
  775.             }
  776.     }
  777.     if (!device_name)
  778.         usage();
  779.     check_mount();        /* trying to check a mounted filesystem? */
  780.     if (repair && !automatic) {
  781.         if (!isatty(0) || !isatty(1))
  782.             die("need terminal for interactive repairs");
  783.         tcgetattr(0,&termios);
  784.         tmp = termios;
  785.         tmp.c_lflag &= ~(ICANON|ECHO);
  786.         tcsetattr(0,TCSANOW,&tmp);
  787.     }
  788.     IN = open(device_name,repair?O_RDWR:O_RDONLY);
  789.     if (IN < 0)
  790.         die("unable to open '%s'");
  791.     for (count=0 ; count<3 ; count++)
  792.         sync();
  793.     read_tables();
  794.  
  795.     /*
  796.      * Determine whether or not we should continue with the checking.
  797.      * This is based on the status of the filesystem valid and error
  798.      * flags and whether or not the -f switch was specified on the 
  799.      * command line.
  800.      */
  801.     printf("%s, %s\n", program_name, program_version);
  802.     if ( !(Super.s_state & MINIX_ERROR_FS) && 
  803.           (Super.s_state & MINIX_VALID_FS) && 
  804.           !force ) {
  805.         if (repair)
  806.             printf("%s is clean, no check.\n", device_name);
  807.         if (repair && !automatic)
  808.             tcsetattr(0,TCSANOW,&termios);
  809.         return retcode;
  810.     }
  811.     else if (force)
  812.         printf("Forcing filesystem check on %s.\n", device_name);
  813.     else if (repair)
  814.         printf("Filesystem on %s is dirty, needs checking.\n",\
  815.             device_name);
  816.  
  817.     check_root();
  818.     check();
  819.     if (verbose) {
  820.         int i, free;
  821.  
  822.         for (i=1,free=0 ; i < INODES ; i++)
  823.             if (!inode_in_use(i))
  824.                 free++;
  825.         printf("\n%6d inodes used (%d%%)\n",(INODES-free-1),
  826.             100*(INODES-free-1)/(INODES-1));
  827.         for (i=FIRSTZONE,free=0 ; i < ZONES ; i++)
  828.             if (!zone_in_use(i))
  829.                 free++;
  830.         printf("%6d zones used (%d%%)\n",(ZONES-free),
  831.             100*(ZONES-free)/ZONES);
  832.         printf("\n%6d regular files\n"
  833.         "%6d directories\n"
  834.         "%6d character device files\n"
  835.         "%6d block device files\n"
  836.         "%6d links\n"
  837.         "%6d symbolic links\n"
  838.         "------\n"
  839.         "%6d files\n",
  840.         regular,directory,chardev,blockdev,
  841.         links-2*directory+1,symlinks,total-2*directory+1);
  842.     }
  843.     if (changed) {
  844.         write_tables();
  845.         printf(    "----------------------------\n"
  846.             "FILE SYSTEM HAS BEEN CHANGED\n"
  847.             "----------------------------\n");
  848.         for (count=0 ; count<3 ; count++)
  849.             sync();
  850.     }
  851.     else if ( repair )
  852.         write_super_block();
  853.     
  854.     if (repair && !automatic)
  855.         tcsetattr(0,TCSANOW,&termios);
  856.  
  857.     if (changed)
  858.           retcode += 3;
  859.     if (errors_uncorrected)
  860.           retcode += 4;
  861.     return retcode;
  862. }
  863.