home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume2 / sys5-fs-analysis / fsanalyze.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-07  |  24.6 KB  |  881 lines

  1. /*
  2.  * fsanalyze.c - file system analyzer - v2.04; 7 January 1988
  3.  *
  4.  * Author   : Michael J. Young
  5.  * USmail   : Software Development Technologies, Inc.
  6.  *            375 Dutton Rd
  7.  *            Sudbury MA 01776
  8.  * UUCP     : {decvax|harvard|linus|mit-eddie}!necntc!necis!mrst!sdti!mjy
  9.  * Internet : mjy%sdti.uucp@harvard.harvard.edu
  10.  *
  11.  * =========================================================================
  12.  * Note : This program has been placed in the public domain to permit
  13.  * unrestricted distribution and use.  I have placed no copyright on it, but
  14.  * I request that you keep me informed about any enhancements and bug fixes
  15.  * you make so I can keep an up-to-date copy for further distribution.
  16.  * =========================================================================
  17.  *
  18.  * fsanalyze is a simple tool that estimates file system fragmentation.  It
  19.  * accomplishes this by scanning the data blocks for each i-node in the 
  20.  * file system, looking for block numbers that are out of sequence.  
  21.  * Fragmentation is then computed as the ratio of out-of-sequence blocks to
  22.  * total data blocks.
  23.  * 
  24.  * fsanalyze also provides statistics regarding the number (and identity) of
  25.  * files that are very large, and especially excessively large directories 
  26.  * (which are very inefficient).
  27.  * 
  28.  * To build fsanalyze the steps are:
  29.  *       cc -O -o fsanalyze fsanalyze.c
  30.  *       mv fsanalyze /bin
  31.  *
  32.  * Usage:
  33.  *    fsanalyze [-flags] special [file] ...
  34.  *
  35.  *    where [flags] include the following:
  36.  *      d   display i-node numbers as they are examined
  37.  *      e   report file size inconsistencies
  38.  *      i   report inodes numbers with double and triple indirection
  39.  *    o   override error checking on file system superblock
  40.  *
  41.  *    If the optional file argument(s) are present, only the specified
  42.  *    individual files on the file system are analyzed.  If absent, the
  43.  *    entire file system is analyzed.
  44.  *
  45.  *    Example:
  46.  *      fsanalyze /dev/dsk/0s2
  47.  *
  48.  *    Since fsanalyze uses the superblock info ON THE DISK, more accurate
  49.  *    results will be returned if sync(1) is executed immediately prior to
  50.  *    fsanalyze.
  51.  */
  52.  
  53. /*
  54.  * Modification History:
  55.  *
  56.  *    Date       Author                   Description
  57.  * -----------  --------  -----------------------------------------------
  58.  * 28 Jul 1987    MJY       Originated
  59.  *  5 Oct 1987    MJY       Capability to analyze individual files,
  60.  *                Added error checking to file system argument,
  61.  *                Added -o flag
  62.  *                Prints out volume and file system name in summary
  63.  * 12 Oct 1987    MJY       Use /etc/fsstat to do file system validity
  64.  *                          checking
  65.  *  9 Nov 1987    MJY       print out warning if file system is mounted
  66.  * 12 Nov 1987    MJY       Volume size statistics now long instead of int
  67.  *  7 Jan 1988    MJY       Modified blk_no() to use l3tol()
  68.  */
  69.  
  70.  
  71. # include <stdio.h>
  72. # include <sys/types.h>
  73. # include <sys/ino.h>
  74. # include <sys/param.h>
  75. # include <sys/filsys.h>
  76. # include <sys/stat.h>
  77.  
  78. # define BLK_SIZE    512        /* block size */
  79. # define IBLK         2        /* block number of first i-node */
  80.  
  81. # define I_BLOCK_FACTOR    32        /* number of i-nodes per block */
  82.  
  83. /*
  84.  * file mode definitions
  85.  */
  86. # define FILE_TYPE(a)     ((a)->di_mode & S_IFMT)
  87. # define IS_SPECIAL(a)    (((a) & S_IFMT == S_IFBLK) || ((a) & S_IFMT == S_IFCHR) || ((a) & S_IFMT == S_IFIFO))
  88.  
  89. /*
  90.  * basic definitions
  91.  */
  92. # define FALSE    0
  93. # define TRUE    1
  94.  
  95. typedef int boolean;
  96.  
  97. /*
  98.  * per-file statistics structure
  99.  */
  100. struct file_data {
  101.     int inode;            /* i-node number */
  102.     long total_blocks;        /* total blocks in file (incl 
  103.                      * indirect blocks */
  104.     long data_blocks;        /* total data blocks in file */
  105.     long potential_seeks;        /* number of potential seeks in file */
  106.     long seeks;            /* actual seeks in file */
  107.     float fragm;            /* fragmentation (seeks/pot.seeks) */
  108.     };
  109.  
  110. FILE *fsys               = NULL;    /* file system under test */
  111. char *special            = NULL;    /* file system device name */
  112.  
  113. /*
  114.  * file system static data
  115.  */
  116. struct filsys super      = {0};        /* holds file system super block */
  117. int fs_type              = 1;        /* file system logical block size
  118.                      * (in 512 byte units) */
  119. long num_inodes          = 0;        /* number of i-nodes in file system */
  120.  
  121. /*
  122.  * calculated global statistics
  123.  */
  124. long blocks              = 0;        /* running block count */
  125. long free_inodes         = 0;        /* number of unused i-nodes */
  126. long potential_seeks     = 0;        /* potential number of disk seeks
  127.                      * during sequential access of a 
  128.                      * file */
  129. long seeks               = 0;        /* actual number of disk seeks
  130.                      * required during sequential access
  131.                      * of a file */
  132. long indirects           = 0;        /* number of files w/ more than
  133.                      * 10 data blocks */
  134. long double_indirects    = 0;        /* number of files w/ more than
  135.                     /* one level of indirection */
  136. long triple_indirects    = 0;        /* number of files w/ more than
  137.                      * two levels of indirection */
  138. int big_directories      = 0;        /* number of directories with
  139.                      * indirection */
  140. int num_directories      = 0;        /* number of directories */
  141. int linked_files         = 0;        /* number of multiply-linked files */
  142. int size_errors          = 0;        /* number of file size discrepancies */
  143.  
  144.  
  145. struct file_data file_log[10] = {0};        /* 10 worst offenders */
  146.  
  147.  
  148. /*
  149.  * fsanalyze command-line flags
  150.  */
  151. boolean report_indirects = FALSE;    /* command line option -- report
  152.                      * files with indirect data blocks */
  153. boolean report_errors    = FALSE;    /* command line option -- report
  154.                      * file size discrepancies */
  155. boolean debug            = FALSE;    /* report i-node #s as they are
  156.                      * examined */
  157. boolean override     = FALSE;    /* override bad fs test */
  158.  
  159.  
  160. /*
  161.  * interface to system error messages
  162.  */
  163. extern int errno;
  164. extern char *sys_errlist[];
  165. extern int sys_nerr;
  166.  
  167. /*
  168.  * usage : prints out a short message describing command usage.  This
  169.  * function does not return.
  170.  */
  171. void usage(){
  172.     printf ("File System analyzer - v2.01\n");
  173.     printf ("Usage:\n   fsanalyze [-[deio]] special [file] ...\n");
  174.     printf ("\n\tIf the [file] argument(s) are missing, the entire\n");
  175.     printf ("\tfile system is scanned.  Otherwise, only the specified\n");
  176.     printf ("\tfiles are examined.  Valid flag are:\n\n");
  177.     printf ("\td\tdisplay i-node numbers as they are examined\n");
  178.     printf ("\te\treport file size inconsistencies\n");
  179.     printf ("\ti\treport data block double and triple indirection\n");
  180.     printf ("\to\toverride error checking on file system argument\n");
  181.     exit(1);
  182.     }
  183.  
  184. /*
  185.  * check_fs : checks the file system to be sure it is not in need of
  186.  * checking.  If the file system is damaged, the function displays a
  187.  * message from fsstat, then returns FALSE.  Othersize, returns TRUE.
  188.  */
  189. boolean check_fs (special)
  190. char *special;
  191. {
  192.     char buffer[256];            /* buffer to hold cmd */
  193.     int fstat;                /* exit status of fsstat */
  194.  
  195.     sprintf (buffer, "/etc/fsstat %s 2>/dev/null", special);
  196.     fstat = system (buffer);
  197.     if (fstat != 0 && !override){
  198.         if (fstat == 512 /* why is this byte-swapped? */){
  199.             fprintf (stderr, "warning: file system is mounted\n");
  200.             }
  201.         else {
  202.             /*
  203.              * run fsstat again to get error message (this is a kludge,
  204.              * but I hope more portable that having fsanalyze do the
  205.              * analysis itself)
  206.              */
  207.             sprintf (buffer, "/etc/fsstat %s", special);
  208.             system(buffer);
  209.             return (FALSE);
  210.             }
  211.         }
  212.     return (TRUE);
  213.     }
  214.  
  215. /*
  216.  * error : prints out a formatted error string to stderr, followed if
  217.  * possible by an appropriate system error message.  Control is then
  218.  * returned to the system with an error status.  This function does not
  219.  * return.
  220.  */
  221. void error (err, str, arg1, arg2)
  222. int err;            /* value of errno at time of call */
  223. char *str;            /* error string */
  224. char *arg1, *arg2;        /* additional printf arguments, if any */
  225. {
  226.     fprintf (stderr, str, arg1, arg2);
  227.     if (err <= sys_nerr && err > 0)
  228.         fprintf (stderr, "%s\n", sys_errlist[err]);
  229.     else
  230.         fprintf (stderr, "unknown error : %d\n", err);
  231.     exit(1);            /* exit with error status */
  232.     }
  233.  
  234. /*
  235.  * init_stats : initializes per-file statistics structure
  236.  */
  237. void init_stats (data, inode)
  238. struct file_data *data;                /* file stats to init */
  239. int inode;                    /* i-node number */
  240. {
  241.     data->inode = inode;
  242.     data->total_blocks = data->data_blocks = 0;
  243.     data->potential_seeks = data->seeks = 0;
  244.     data->fragm = 0.0;
  245.     }
  246.  
  247. /*
  248.  * init : parses command line flags and the file system device name, then
  249.  * reads the file system super block.  init returns the number of the
  250.  * parameter AFTER the file system device name.  All subsequent parameters
  251.  * are assumed to be specific files to be tested.
  252.  */
  253. int init (argc, argv)
  254. int argc;
  255. char *argv[];
  256. {
  257.     int i;                    /* loop counter */
  258.     char *cp;                /* cmd-line flag pointer */
  259.     boolean special_found = FALSE;        /* TRUE = found special arg */
  260.     
  261.     for (i = 1; i < argc && !special_found; i++){
  262.         cp = argv[i];
  263.         if (*cp == '-'){
  264.             while (*++cp){
  265.                 switch (*cp){
  266.  
  267.                         /* report i-nodes with
  268.                          * one or more levels of
  269.                          * indirection */
  270.                     case 'i':
  271.                         report_indirects = TRUE;
  272.                         break;
  273.  
  274.                         /* report file size errors */
  275.                     case 'e':
  276.                         report_errors = TRUE;
  277.                         break;
  278.  
  279.                         /* debugging flag */
  280.                     case 'd':
  281.                         debug = TRUE;
  282.                         break;
  283.  
  284.                         /* override bad fs test */
  285.                     case 'o':
  286.                         override = TRUE;
  287.                         break;
  288.                     default:
  289.                         fprintf (stderr, "illegal option : %c\n", *cp);
  290.                         usage();
  291.                     }
  292.                 }
  293.             }
  294.         else {
  295.             special = argv[i];
  296.             special_found = TRUE;
  297.             }
  298.         }
  299.     if (special == NULL)usage();        /* no fs parameter */
  300.  
  301.     /*
  302.      * check for good file system (let fsstat do the dirty work!)
  303.      */
  304.     if (check_fs (special)){
  305.  
  306.         /*
  307.          * open file system and read the super block
  308.          */
  309.         if ((fsys=fopen (special, "r")) == NULL){
  310.             error (errno, "error opening \"%s\"\n", special);
  311.             /* NOTREACHED */
  312.             }
  313.         if (fseek (fsys, 512L, 0)){
  314.             error (errno, "error seeking superblock");
  315.             /* NOTREACHED */
  316.             }
  317.         if (fread (&super, sizeof (struct filsys), 1, fsys) != 1){
  318.             error (errno, "error reading superblock");
  319.             /* NOTREACHED */
  320.             }
  321.  
  322.         num_inodes = (super.s_isize-2) * 16;
  323.         if (super.s_magic != FsMAGIC)
  324.             fs_type = Fs1b;
  325.         else fs_type = super.s_type;
  326.  
  327.         return i;        /* return # of next argument to be processed */
  328.         }
  329.     else {
  330.         exit(1);        /* bad file system, error status */
  331.         }
  332.     }
  333.  
  334. /*
  335.  * blk_no : given a pointer to a 3-byte binary number, returns a (long)
  336.  * block number.  Used to access the first 10 data block numbers of an
  337.  * i-node.  The function l3tol is used for portability.
  338.  */
  339. daddr_t blk_no (off)
  340. unsigned char off[];                /* 3-byte offset */
  341. {
  342.     extern void l3tol();
  343.     daddr_t temp = 0;
  344.     int n;
  345.  
  346.     l3tol (&temp, off, 1);
  347.     return temp;
  348.     }
  349.  
  350. /*
  351.  * get_inodes : given an initial i-node number, reads a block of i-nodes
  352.  * into an i-node array.
  353.  */
  354. void get_inodes (in, inp, num)
  355. int in;                        /* inode number */
  356. int num;                    /* # inodes to get */
  357. struct dinode *inp;                /* buffer to hold info */
  358. {
  359.     long position;                /* computed position of the
  360.                          * first requested i-node */
  361.  
  362.     position = (IBLK * BLK_SIZE * fs_type) + 
  363.            (sizeof (struct dinode) * ((long)in-1));
  364.  
  365.     if (fseek (fsys, position, 0)){
  366.         error (errno, "\nerror seeking inode %d, pos = %ld\n", in, position);
  367.         /* NOTREACHED */
  368.         }
  369.     else {
  370.         if (fread (inp, sizeof (struct dinode), num, fsys) != num){
  371.             error (errno, "\nerror reading inode %d\n", in);
  372.             /* NOTREACHED */
  373.             }
  374.         }
  375.     }
  376.  
  377. /*
  378.  * check_indirects : scans a block containing data block numbers, looking
  379.  * for block numbers that are not sequential.
  380.  */
  381. daddr_t check_indirects (block, cur_pos, data)
  382. daddr_t block;                    /* indirect block to check */
  383. daddr_t cur_pos;                /* current block offset */
  384. struct file_data *data;                /* current file statistics */
  385. {
  386.     daddr_t indirect_blk[256];        /* holds an indirect block */
  387.     int num_blocks;                /* number of data blocks in
  388.                          * an indirect block */
  389.     daddr_t pos;                /* current data block */
  390.     daddr_t new_pos;            /* next data block */
  391.     int i;                    /* loop counter */
  392.  
  393.     num_blocks = 128 * fs_type;
  394.     data->total_blocks++;
  395.  
  396.     /*
  397.      * get the indirect block
  398.      */
  399.     if (fseek (fsys, block * 512L * fs_type, 0)){
  400.         error (errno, "\nerror seeking indirect block %ld\n", block);
  401.         /* NOTREACHED */
  402.         }
  403.     if (fread (indirect_blk, sizeof (daddr_t), num_blocks, fsys) != num_blocks){
  404.         error (errno, "\nerror reading indirect block %ld\n", block);
  405.         /* NOTREACHED */
  406.         }
  407.     pos = cur_pos;
  408.  
  409.     /*
  410.      * scan the data blocks looking for numbers out of sequence
  411.      */
  412.     for (i = 0; i < num_blocks; i++){
  413.         new_pos = indirect_blk [i];
  414.         if (new_pos == 0){
  415.             return pos;
  416.             }
  417.         data->data_blocks++;
  418.         data->total_blocks++;
  419.         data->potential_seeks++;
  420.         if (new_pos != pos + 1){
  421.             data->seeks++;
  422.             }
  423.         pos = new_pos;
  424.         }
  425.     return pos;
  426.     }
  427.  
  428. /*
  429.  * check_double_indirects : scans a block containing a list of indirect
  430.  * blocks, checking for data block numbers that are out of sequence.
  431.  */
  432. daddr_t check_double_indirects (block, cur_pos, data)
  433. daddr_t block;                    /* indirect block to check */
  434. daddr_t cur_pos;                /* current block offset */
  435. struct file_data *data;                /* current file statistics */
  436. {
  437.     daddr_t dindirect_blk[256];        /* holds a double-indirect
  438.                          * block */
  439.     int i;                    /* loop counter */
  440.     int num_blocks;                /* number of indirect blocks
  441.                          * in a d-i block */
  442.  
  443.     num_blocks = 128 * fs_type;
  444.     data->total_blocks++;
  445.  
  446.     /*
  447.      * the double-indirect block itself should be in sequence with the
  448.      * data blocks
  449.      */
  450.     data->potential_seeks++;
  451.     if (block != cur_pos + 1){
  452.         data->seeks++;
  453.         }
  454.  
  455.     /*
  456.      * get the d-i block
  457.      */
  458.     if (fseek (fsys, block * 512L * fs_type, 0)){
  459.         error (errno, "\nerror seeking double indirect block %ld\n",
  460.              block);
  461.         /* NOTREACHED */
  462.         }
  463.     if (fread (dindirect_blk, sizeof (daddr_t), num_blocks, fsys) != num_blocks){
  464.         error (errno, "\nerror reading double indirect block %ld\n",
  465.              block);
  466.         /* NOTREACHED */
  467.         }
  468.  
  469.     /*
  470.      * scan through the d-i block
  471.      */
  472.     for (i = 0; i < num_blocks; i++){
  473.         if (dindirect_blk[i] == 0){
  474.             break;
  475.             }
  476.         cur_pos = check_indirects (dindirect_blk[i],
  477.                        block,
  478.                        data);
  479.         }
  480.     return cur_pos;
  481.     }
  482.  
  483. /*
  484.  * check_triple_indirects : scans a block containing a list of double
  485.  * indirect blocks, looking for data block numbers that are out of sequence.
  486.  */
  487. daddr_t check_triple_indirects (block, cur_pos, data)
  488. daddr_t block;                    /* indirect block to check */
  489. daddr_t cur_pos;                /* current block offset */
  490. struct file_data *data;                /* current file statistics */
  491. {
  492.     daddr_t tindirect_blk[256];        /* holds a triple-indirect
  493.                          * block */
  494.     int i;                    /* loop counter */
  495.     int num_blocks;                /* number of double-indirect
  496.                          * blocks in a triple-i blk */
  497.  
  498.  
  499.     num_blocks = 128 * fs_type;
  500.     data->total_blocks++;
  501.  
  502.     /*
  503.      * the triple-indirect block itself should be in sequence with the
  504.      * data blocks
  505.      */
  506.     data->potential_seeks++;
  507.     if (block != cur_pos + 1){
  508.         data->seeks++;
  509.         }
  510.  
  511.     /*
  512.      * get the t-i block
  513.      */
  514.     if (fseek (fsys, block * 512L * fs_type, 0)){
  515.         error (errno, "\nerror seeking triple indirect block %ld\n",
  516.              block);
  517.         /* NOTREACHED */
  518.         }
  519.     if (fread (tindirect_blk, sizeof (daddr_t), num_blocks, fsys) != num_blocks){
  520.         error (errno, "\nerror reading triple indirect block %ld\n",
  521.              block);
  522.         /* NOTREACHED */
  523.         }
  524.  
  525.     /*
  526.      * scan through the t-i block
  527.      */
  528.     for (i = 0; i < num_blocks; i++){
  529.         if (tindirect_blk[i] == 0){
  530.             break;
  531.             }
  532.         cur_pos = check_double_indirects (tindirect_blk[i],
  533.                           block,
  534.                           data);
  535.         }
  536.     return cur_pos;
  537.     }
  538.  
  539. /*
  540.  * check_file : scans the data block numbers of an i-node, looking for
  541.  * block numbers that are out of sequence, and would thus result in excess
  542.  * track-to-track seeking.  This function also checks directory files for
  543.  * indirection (more than 10 data blocks), and performs a simple consistency
  544.  * check on all file sizes
  545.  */
  546. void check_file (inode, inode_number, data)
  547. struct dinode *inode;                /* inode info structure */
  548. int inode_number;                /* inode number to be
  549.                          * checked */
  550. struct file_data *data;                /* current file statistics */
  551. {
  552.     daddr_t pos;                /* current block */
  553.     daddr_t new_pos;            /* next block in file */
  554.     int i;                    /* loop counter */
  555.     long file_size;                /* file size computed by
  556.                          * actual byte count */
  557.  
  558.     pos = blk_no (&inode->di_addr[0]);    /* first data block */
  559.  
  560.     if (inode->di_size == 0 || pos == 0)return; /* ignore 0-size files */
  561.  
  562.     data->data_blocks = data->total_blocks = 1;
  563.  
  564.     /*
  565.      * do some simple-minded statistics gathering
  566.      */
  567.     if (FILE_TYPE (inode) == S_IFDIR){ /* got a directory */
  568.         num_directories++;
  569.         }
  570.     if (inode->di_nlink > 1){        /* multi-linked files */
  571.         linked_files++;
  572.         }
  573.  
  574.     /*
  575.      * scan the data blocks looking for numbers out of sequence
  576.      */
  577.     for (i = 1; i < 10; i++){
  578.         new_pos = blk_no (&inode->di_addr[i*3]);
  579.         if (new_pos == 0){        /* end of file */
  580.             break;
  581.             }
  582.         data->data_blocks++;
  583.         data->total_blocks++;
  584.         data->potential_seeks++;
  585.         if (new_pos != pos + 1){    /* out of sequence ? */
  586.             data->seeks++;
  587.             }
  588.         pos = new_pos;
  589.         }
  590.  
  591.     /*
  592.      * block 10, if non-zero, is the number of the block which contains
  593.      * the next (128 * fs_type) data block numbers.  It should also be
  594.      * in sequence with the data blocks.  Block 10 is called an
  595.      * "indirect" block.
  596.      */
  597.     if (blk_no (&inode->di_addr[10*3])){
  598.         indirects++;
  599.  
  600.         /*
  601.          * if a directory contains indirection, it is too large for
  602.          * efficient access.  Report it.
  603.          */
  604.         if (FILE_TYPE (inode) == S_IFDIR){
  605.             printf ("inode %d is a large directory\n", inode_number);
  606.             big_directories++;
  607.             }
  608.         pos = check_indirects (blk_no (&inode->di_addr[10*3]),
  609.                        pos,
  610.                        data);
  611.         }
  612.  
  613.     /*
  614.      * block 11, if non-zero, is the number of the block which contains
  615.      * the next (128 * fs_type) INDIRECT block numbers.  It should also be
  616.      * in sequence with the data blocks.  Block 11 is called a "double-
  617.      * indirect" block.
  618.      */
  619.     if (blk_no (&inode->di_addr[11*3])){
  620.         double_indirects++;
  621.         if (report_indirects){
  622.             printf ("double indirection : %d\n", inode_number);
  623.             }
  624.         pos = check_double_indirects (blk_no (&inode->di_addr[11*3]),
  625.                           pos,
  626.                           data);
  627.         }
  628.  
  629.     /*
  630.      * block 12, if non-zero, is the number of the block which contains
  631.      * the next (128 * fs_type) DOUBLE-INDIRECT block numbers.  It should
  632.      * also be in sequence with the data blocks.  Block 12 is called a
  633.      * "triple-indirect" block.
  634.      */
  635.     if (blk_no (&inode->di_addr[12*3])){
  636.         triple_indirects++;
  637.         if (report_indirects){
  638.             printf ("triple indirection : %d\n", inode_number);
  639.             }
  640.         pos = check_triple_indirects (blk_no (&inode->di_addr[12*3]),
  641.                           pos,
  642.                           data);
  643.         }
  644.  
  645.     /*
  646.      * do a simple check to detect possible file-size errors (a la
  647.      * fsck phase 1)
  648.      */
  649.     file_size = (inode->di_size + (512 * fs_type - 1)) / (512 * fs_type);
  650.     if (file_size != data->data_blocks){
  651.         size_errors++;
  652.         if (report_errors){
  653.             printf ("inode %d, inconsistent file size : actual blocks = %ld, computed = %ld (%ld bytes)\n",
  654.                 inode_number, data->data_blocks, 
  655.                 file_size, inode->di_size);
  656.             }
  657.         }
  658.     }
  659.  
  660. /*
  661.  * log_stats : updates global statistics based on the current file statistics.
  662.  * The current file is then checked to see if it qualifies as one of the 10
  663.  * worst offenders (i.e., most fragmented) encountered thus far.  The 10 worst
  664.  * offenders are determined based on their absolute number of disk seeks
  665.  * required to read the entire file.  Such an absolute test (viz a viz a
  666.  * relative percentage test) ensures that very small, but fragmented, files
  667.  * will not clutter the output.
  668.  */
  669. void log_stats (data)
  670. struct file_data *data;                /* file statistics to be
  671.                          * logged */
  672. {
  673.     int i, j;                /* loop counters */
  674.  
  675.  
  676.     /*
  677.      * update global statistics
  678.      */
  679.     blocks += data->total_blocks;
  680.     potential_seeks += data->potential_seeks;
  681.     seeks += data->seeks;
  682.     data->fragm = data->potential_seeks ? (float)data->seeks/(float)data->potential_seeks : 0.0;
  683.  
  684.     /*
  685.      * update 10 worst offender array
  686.      */
  687.     for (i = 0; i < 10; i++){
  688.         if (data->seeks > file_log[i].seeks){
  689.             for (j = 9; j > i; j--){
  690.                 file_log[j] = file_log[j-1];
  691.                 }
  692.             file_log[i] = *data;
  693.             break;
  694.             }
  695.         }
  696.     }
  697.  
  698. /*
  699.  * scan : scan through each i-node of a file system, compiling statistics
  700.  * regarding fragmentation and indirection.
  701.  */
  702. void scan (){
  703.     int    i, j;                /* loop counters */
  704.     struct dinode i_node[I_BLOCK_FACTOR];    /* holds a block of inodes */
  705.     struct file_data data;            /* per-inode statistics */
  706.  
  707.     for (i = 1; i < num_inodes; i+=I_BLOCK_FACTOR){
  708.  
  709.         /*
  710.          * for efficiency, read a block of i-nodes at a time
  711.          */
  712.         get_inodes (i, i_node, I_BLOCK_FACTOR);
  713.  
  714.         /*
  715.          * scan through each i-node that was read in
  716.          */
  717.         for (j = 0; i+j < num_inodes && j < I_BLOCK_FACTOR; j++){
  718.             if (debug){
  719.                 printf ("inode %d\r", i+j);
  720.                 }
  721.             if (i+j <= 1)continue;    /* don't scan i-node 1 */
  722.             if (i_node[j].di_mode != 0){ /* unused i-node ? */
  723.                 init_stats (&data, i+j);
  724.                 check_file (&i_node[j], i+j, &data); /* scan blocks in file */
  725.                 log_stats (&data);
  726.                 }
  727.             else {
  728.                 free_inodes++;
  729.                 }
  730.             }
  731.         }
  732.     }
  733.  
  734. /*
  735.  * print_report : calculates percentages and prints a summary report of
  736.  * file system statistics
  737.  */
  738. void print_report (){
  739.     long    num_files;            /* number of inodes used
  740.                          * in file system */
  741.     int    i;                /* loop counter */
  742.     
  743.     float    fragm,                /* percent fragmentation */
  744.         ind_p,                /* percent indirections */
  745.         dind_p,                /* percent double indirects */
  746.         tind_p,                /* percent triple indirects */
  747.         bused_p,            /* percent data blocks used */
  748.         iused_p,            /* percent inodes used */
  749.         bdir_p;                /* percent of directories
  750.                          * that have more than 10
  751.                          * blocks */
  752.  
  753.     /*
  754.      * calculate percentages for report
  755.      */
  756.     fragm = potential_seeks ? (float)seeks/(float)potential_seeks : 0.0;
  757.     bused_p = (float)blocks/(float)(blocks+super.s_tfree);
  758.     num_files = num_inodes - free_inodes;
  759.     iused_p = (float)num_files/(float)num_inodes;
  760.     ind_p = num_files ? (float)indirects/(float)num_files : 0.0;
  761.     dind_p = num_files ? (float)double_indirects/(float)num_files : 0.0;
  762.     tind_p = num_files ? (float)triple_indirects/(float)num_files : 0.0;
  763.     bdir_p = num_directories ? (float)big_directories/(float)num_directories : 0.0;
  764.  
  765.     /*
  766.      * print out report
  767.      */
  768.     printf ("\n\nFile system name = \"%.6s\", Volume name = \"%.6s\"\n",
  769.         super.s_fname, super.s_fpack);
  770.     printf ("File system logical block size = %d bytes\n", 512 * fs_type);
  771.     printf ("Volume Size = %ld blocks (%ld bytes)\n",
  772.         super.s_fsize, super.s_fsize * 512L * fs_type);
  773.     printf ("\t%u blocks reserved for super block and inodes\n", super.s_isize);
  774.     printf ("\t%lu blocks reserved for data\n", 
  775.         super.s_fsize - super.s_isize);
  776.     printf ("Total inodes = %d\n", num_inodes);
  777.     printf ("%.2f%% inodes used (%ld used, %ld free)\n", 
  778.         iused_p*100, num_inodes - free_inodes, free_inodes);
  779.     printf ("%.2f%% data blocks used (%ld used, %ld free)\n",
  780.         bused_p*100, blocks, super.s_tfree);
  781.     printf ("%d directories\n", num_directories);
  782.     printf ("%d multiply-linked files\n", linked_files);
  783.     printf ("\nFragmentation         = %.2f%%\n", fragm*100);
  784. /*    printf ("(%ld seeks of %ld potential)\n", seeks, potential_seeks); */
  785.     printf ("Indirects             = %ld (%.2f%%)\n",
  786.         indirects, ind_p*100);
  787.     printf ("Double indirects      = %ld (%.2f%%)\n",
  788.         double_indirects, dind_p*100);
  789.     printf ("Triple indirects      = %ld (%.2f%%)\n",
  790.         triple_indirects, tind_p*100);
  791.     printf ("Oversized directories = %d (%.2f%%)\n",
  792.         big_directories, bdir_p*100);
  793.     printf ("10 worst offenders:\n");
  794.     printf ("   i-node   Size  Fragments    %%\ti-node   Size  Fragments    %%\n");
  795.     for (i = 0; i < 5; i++){
  796.         if (file_log[i].inode != 0){
  797.             printf ("   %6d %6ld   %6ld   %6.2f%%",
  798.                 file_log[i].inode,
  799.                 file_log[i].data_blocks,
  800.                 file_log[i].seeks+1,
  801.                 file_log[i].fragm*100);
  802.             if (file_log[i+5].inode != 0){
  803.                 printf ("\t%6d %6ld   %6ld   %6.2f%%",
  804.                     file_log[i+5].inode,
  805.                     file_log[i+5].data_blocks,
  806.                     file_log[i+5].seeks+1,
  807.                     file_log[i+5].fragm*100);
  808.                 }
  809.             printf ("\n");
  810.             }
  811.         else break;
  812.         }
  813.     }
  814.  
  815. /*
  816.  * anal_file : analyzes a single file for fragmentation.
  817.  */
  818. void anal_file (ino, fname)
  819. int ino;                    /* i-node number */
  820. char *fname;                    /* filename of this inode */
  821. {
  822.     struct file_data data;            /* current file statistics */
  823.  
  824.     struct dinode i_node;            /* current inode data */
  825.  
  826.     get_inodes (ino, &i_node, 1);
  827.     init_stats(&data, ino);
  828.     check_file (&i_node, ino, &data);
  829.     log_stats (&data);
  830.     printf ("   %-14s\t%6d    %6ld   %6ld   %6.2f%%\n",
  831.         fname, data.inode, data.seeks+1, 
  832.                        data.total_blocks, data.fragm*100);
  833.     }
  834.  
  835. main (argc, argv)
  836. int argc;
  837. char *argv[];
  838. {
  839.     int next_param;
  840.     struct stat f_stat;
  841.  
  842.     /*
  843.      * perform various initialization functions
  844.      */
  845.     next_param = init (argc, argv);
  846.  
  847.     if (next_param == argc){
  848.         /*
  849.          * no individual files to check, scan entire file system
  850.          */
  851.         printf ("Analyzing file system %s...\n", special);
  852.  
  853.         /*
  854.          * scan through all i-nodes in the file system
  855.          */
  856.         scan();
  857.  
  858.         /*
  859.          * print out statistics summary
  860.          */
  861.         print_report();
  862.         }
  863.     else {
  864.         /*
  865.          * scan individual files instead of entire file system
  866.          */
  867.         printf ("        Name      \ti-node    Fragments  Size      %%\n");
  868.         for (; next_param < argc; next_param++){
  869.             if (stat (argv[next_param], &f_stat) != 0){
  870.                 error (errno, "error opening \"%s\"\n",
  871.                        argv[next_param]);
  872.                 }
  873.             else {
  874.                 if (!IS_SPECIAL (f_stat.st_mode)){
  875.                     anal_file (f_stat.st_ino, argv[next_param]);
  876.                     }
  877.                 }
  878.             }
  879.         }
  880.     }
  881.