[comp.sources.misc] v05i069: Fsanalyze Version 4.1, Part 2/3

mjy@sdti.sdti.com (Michael J. Young) (12/07/88)

Posting-number: Volume 5, Issue 69
Submitted-by: "Michael J. Young" <mjy@sdti.sdti.com>
Archive-name: fsanalyze4.1/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 3)."
# Contents:  Info fsanalyze.8 fsanalyze.h init.c report.c
# Wrapped by mjy@sdti on Wed Nov 30 15:54:15 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Info -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Info\"
else
echo shar: Extracting \"Info\" \(8768 characters\)
sed "s/^X//" >Info <<'END_OF_Info'
XInterpreting File System Fragmentation:
X
X   In my experience, when a file system approaches 15-20% fragmentation,
X   performance has already degraded sufficiently to warrant tuning it.
X   On the other hand, my system has very slow disks.  Faster disks may be
X   able to absorb the additional overhead without much noticeable degradation.
X
X   Remember also that the most fragmented files are most likely those which
X   have been created most recently (and therefore most likely to be used
X   frequently).  So even though the total fragmentation seems low, the files
X   that are used most frequently may be very fragmented.  Fsanalyze can tell
X   you which files are most fragmented, making it possible to fix them
X   without taking the time to rebuild the entire file system.  For hints on
X   how to do this, I have included a message at the end of this file that
X   was recently posted to the net by Charles Hedrick
X   (hedrick@athos.rudgers.edu).
X
X   The BSD file system is designed to minimize fragmentation at the expense
X   of CPU cycles.  Therefore, unless the file system is too full, BSD file
X   systems should never fragmentation over a few percent, and average seek
X   distance should always be very small.  BSD file systems allocation
X   strategy may be set to minimize allocation time or disk fragmentation.
X   Fsanalyze could be used to determine the relative benefits/costs of
X   each strategy.
X
X   On standard file systems, fsanalyze sometimes will report fragmentation
X   even for clean (i.e., tuned) file systems.  The telltale symptom is a
X   large fragmentation percentage but an average seek distance of exactly
X   1.0 cylinders.  There are a couple of reasons for this behavior.
X
X   First of all, on standard file systems at least, mkfs is not very
X   smart in figuring out the optimum order of blocks on the free list.
X   Specifically, mkfs assumes that the first data block is at the
X   beginning of a cylinder, whether it is or not.  If it is not (because
X   the user wasn't very careful about chosing the number of inodes
X   in the file system), the interleaving algorithm mkfs uses may end
X   up causing "sequential" data blocks to jump back and forth across
X   a cylinder boundary.  Fsanalyze correctly detects this suboptimal
X   block placement as excess disk seeks.  Re-mkfs-ing the file system
X   with a number of inodes that uses disk cylinders more effectively
X   will improve things a great deal.
X
X   Another source of excess disk seeks (with distance of 1 cylinder) is
X   when a file is placed such that it spans a cylinder even though it
X   is physically capable of residing entirely on one cylinder.  This
X   is just a fact of life on standard file systems, but it shouldn't
X   show up very often on BSD systems.  I don't see any way of improving
X   file placement at this level except through the use of a more intelligent
X   disk tuning strategy.
X
XPorting fsanalyze to other systems:
X   Fsanalyze takes its information on the structure of the file system
X   from <sys/param.h>, <sys/filsys.h> and <sys/ino.h> (or their equivalents).
X   As long
X   as these files (or equivalent) are present on your system, porting
X   should be straightforward.  When looking for the equivalent of these
X   files on your system, be sure to get the disk-based versions, rather
X   than the in-memory structures -- they are different.  You may have to
X   modify the #include directives in fsanalyze.h to reflect the structure
X   of your /usr/include directory.
X
X   In the file fsconfig.h are a number of macro definitions which provide
X   common access to file system structures for various systems.  The
X   vast majority of the changes required to port fsanalyze should be made
X   here.  There are currently two major sets of macros, those for
X   BSD-derived file systems, and those for AT&T-derivatives.  Choose the
X   macro set which most closely reflects your file system, and make
X   OS-specific changes based on the _OS_TYPE macro.  Feel free to
X   create new os-types as needed!
X
X   I attempted to use types defined in /usr/include/sys/types.h wherever
X   possible to ensure portability.  I don't think I made any assumptions
X   about sizeof(int) == sizeof(long) == sizeof(char *), so there should
X   be no problems there.
X
X   Please try to make changes to the source using #defines and #ifdefs
X   wherever possible, and please email me the changes you make along with
X   a description of the system you ported it to (and problems encountered),
X   so I can merge all the changes into a single copy.
X
X   Good luck!
X
X------------------------------------------------------------------------------
XThe following is a message posted to the net recently that gives details
Xof how to defragment individual files under System V.  This method should
Xalso work for any version of unix which has the fsck -s option (everything
Xsince Version 7, I think).  This won't work for BSD file systems.
X
XFrom mrst!apollo!ulowell!dandelion!necntc!ames!ncar!boulder!sunybcs!rutgers!aramis.rutgers.edu!athos.rutgers.edu!hedrick Mon Apr 18 10:23:50 EDT 1988
XArticle 459 of comp.unix.microport:
XPath: sdti!mrst!apollo!ulowell!dandelion!necntc!ames!ncar!boulder!sunybcs!rutgers!aramis.rutgers.edu!athos.rutgers.edu!hedrick
X>From: hedrick@athos.rutgers.edu (Charles Hedrick)
XNewsgroups: comp.unix.microport
XSubject: disk fragmentation
XMessage-ID: <Apr.12.21.47.44.1988.21023@athos.rutgers.edu>
XDate: 13 Apr 88 01:47:00 GMT
XOrganization: Rutgers Univ., New Brunswick, N.J.
XLines: 63
X
X
XToday I called the Microport support people to see if they could do
Xsomething to help the slow startup I'm seeing for long programs,
Xparticularly Emacs (actually Jove).  I suspected that the file system
Xwas badly fragmented, and thus that the file was inefficiently ordered
Xon the disk.  They gave me some advice that I thought is worth passing
Xon.  Apparently System V doesn't bother to keep the free list in
Xorder.  So after time, it begins to get randomized.  The result is
Xthat reading a long file requires the disk drive to skip all around
Xthe disk.  The only way to completely solve this problem is to
Xreorganize the disk: either do a backup, recreate the file system
X(i.e. do the mkfs again) and reload from the backup, or use dcopy to
Xcopy it to a fresh file system.  However fsck's -s option provides a
Xfairly good way of defragmenting a live file system.  Note that this
Xdoesn't reorder files that are already randomized.  What it does do is
Xsort the free list.  So any new files you create after doing this are
Xreasonably organized.  What I tried was doing fsck -s, the copying a
Xfew programs that seemed to be loading slowly, and finally renaming
Xthe copy to the original name.  (Warning: copy all the files first,
Xbefore you get rid of any of the originals.  As soon as you remove any
Xof the originals, you'll be putting those random blocks right back
Xonto the free list.)  The result is that reading through emacs went
Xfrom 18 seconds to 4 seconds.  I used getblks (a program from the bbs
Xthat shows the block numbers of all the blocks in a file) on both
Xcopies of the file, and the difference is drastic.  I wouldn't quite
Xuse the block numbers of the original as a random number generator,
Xbut they do skip around alarmingly.  The copy has only a few jumps.
XPresumably if I did a backup and restore things would be slightly
Xbetter yet, as those remaining jumps would be gone.  But it's much
Xeasier to run fsck regularly than to dump the file system and restore
Xit regularly.
X
XFor those who find my wording ambiguous, he is roughly what I did to
Xdefragment files /usr/local/bin/emacs and /usr/local/bin/kermit.  (I
Xdidn't write the steps down as I did them, so something could be
Xmissing, but this is fairly close.)
X
X1) reboot from the standalone boot floppy
X2) fsck /dev/rdsk/0s2.  Do this until it runs without errors
X3) fsck -s /dev/rdsk/0s2.  This sorts the free list.
X4) /etc/mount /dev/dsk/0s2 /mnt    (this is /usr)
X5) cd /mnt/local/bin
X6) cp emacs emacs.new
X   cp kermit kermit.new
X  ... etc. for the group I files I wanted to fix
X   mv emacs.new emacs
X   mv kermit.new kermit
X  ... note that it is critical to do all the cp's before any of
X  the mv's.
X7) cd /
X8) /etc/umount /dev/dsk/0s2
X9) fsck -s /dev/rdsk/0s2.  (This is because the process above
X   freed a bunch of files with random junk in them.  I wanted
X   to start off with my free list in good shape.  This isn't
X   strictly necessary.
X10) sync
X11) reboot from hard disk normally
X
XThis worked because there were just a few big files that needed to be
Xdefragmented.  If I had a huge number of files that all were causing
Xperformance problems, it would be easier to dump the disk and recreate
Xit.  Of course if I did the fsck -s regularly enough, I might be
Xable to prevent the problem in the first place.
X
END_OF_Info
if test 8768 -ne `wc -c <Info`; then
    echo shar: \"Info\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f fsanalyze.8 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"fsanalyze.8\"
else
echo shar: Extracting \"fsanalyze.8\" \(7656 characters\)
sed "s/^X//" >fsanalyze.8 <<'END_OF_fsanalyze.8'
X.TH FSANALYZE 8
X.ad b
X.SH NAME
Xfsanalyze \- a file system analyzer tool
X.SH SYNOPSIS
X.B fsanalyze
X[
X.B \-deiov
X[
X.B \-b#
X] [
X.B \-c#
X] [
X.B \-g#
X] ]
X.B special
X[
X.B files
X]
X.SH DESCRIPTION
X.I Fsanalyze
Xis a simple tool that estimates file or file system fragmentation.  It accomplishes
Xthis by scanning the data blocks of each inode looking for block numbers that
Xare out of sequence.  In effect, it is counting the number of disk seeks
Xrequired to read a file sequentially.  Fragmentation is then computed as the
Xratio of actual "seeks" to the potential number "seeks" which would be
Xrequired if the file were completely fragmented.
X.LP
XIf the optional \fBfiles\fR arguments are omitted,
X.I fsanalyze
Xwill analyze the entire file system specified by the \fBspecial\fR argument.
X\fBSpecial\fR must be a block-oriented file system device.
X.LP
XIn addition to fragmentation,
X.I fsanalyze
Xreports other useful file system statistics.  These include:
X.IP "Fragmentation" 21
XThe number of additional disk seeks necessary to access all files in the
Xfile system sequentially.  Expressed as a percentage of the total possible
Xseeks if the file system were maximally fragmented.
X.IP "Average Seek Distance" 21
XThe average distance, in cylinders, that the disk head must travel when
Xseeking due to file fragmentation.
X.IP "Rotation Delay" 21
XEven when data blocks reside on the same cylinder of the disk, their relative
Xplacement may be such that extra delays due to disk rotation result.  A
Xheuristic is used to determine how many blocks are non-optimally placed within
Xa cylinder.  Expressed as a percentage of the total possible number of
Xdelays if the file system were maximally fragmented.
X.IP "Multiply-linked Files" 21
XThe number of inodes that are linked to more than one file name.  Expressed as
Xan absolute number and a percentage of the total number of active inodes.
X.IP "Directories" 21
XThe number of inodes that are being used as directories.  Expressed as
Xan absolute number and a percentage of the total number of active inodes.
X.IP "Oversized Directories" 21
XIf a directory grows large enough to require indirection, file searches
Xare slowed down significantly.  This metric reports the number of directories
Xthat require indirection.  Expressed as an absolute number and a percentage
Xof all the directories on the file system.
X.IP "Special Files" 21
XThe number of inodes that are actually special (character or block devices).
XExpressed as an absolute number and a percentage of all active inodes.
X.IP "Indirect Files" 21
XThe number of files that require at least one level of indirection.  Also
Xincludes files that require multiple levels of indirection.  Expressed as
Xan absolute number and  a percentage of all active inodes.
X.IP "Double Indirects" 21
XThe number of files that require at least two levels of indirection.  Expressed
Xas an absolute number and a percentage of all active inodes.
X.IP "Triple Indirects" 21
XThe number of files that require three levels of indirection (the maximum).
XExpressed as an absolute number and a percentage of all active inodes.
X.IP "Indirection Blks" 21
XThe total number of data blocks that are being used to hold indirect blocks.
XThese blocks are unavailable for data, and represent file system overhead.
XExpressed as an absolute number and a percentage of the total number of
Xdata blocks in the file system.
X.IP "Sparse Files" 21
XIn Unix, it is possible to create files with "holes", i.e., no data, in the
Xmiddle.  This can be done, for instance, by fseek-ing past the end of file
Xand writing to the file.  Unix will not allocate data blocks for parts of
Xthe file which have never been written.  Reading from an unallocated block
Xreturns all zeros.  This can be used to great advantage, since large yet
Xsparse files do not take up any more disk space than needed.  However, sparse
Xfiles are often created inadvertently, and can cause problems during backups
Xif the backup utility doesn't handle them correctly.  Expressed as an absolute
Xnumber and a percentage of all active inodes.
X.IP "Unused Bytes in Last Blocks" 21
XThe last block of a file is rarely completely full.  Because disk space is
Xonly allocated in blocks, the unused bytes in
Xthese blocks are not available for use by other files.  This represents
Xanother source of file system overhead.  Expressed as an absolute number and
Xa percentage of the file system size.
X.LP
X.I Fsanalyze
Xalso lists the 10 most fragmented inodes in the file system.
XThey are listed in decreasing order of fragmentation
Xbased on the absolute number of fragments.  For example a 100-block file
Xthat contains 40 individual fragments is 39.39% fragmented (39 seeks / 99
Xpotential seeks), but is listed before a 2-block file that contains 2
Xfragments (100% fragmented).  This prevents very small, but fragmented, files
Xfrom being listed before larger files, which probably have a more significant
Ximpact on file system throughput.
X.LP
XIf the \fBfiles\fR argument is present,
X.I fsanalyze
Xwill report the fragmentation of the designated files only.
X.LP
XBefore performing any analysis,
X.I fsanalyze
Xchecks the file system integrity.  If it is available,
X.I fsanalyze
Xwill make use of the utility
X.I fsstat (1M).
XOtherwise,
X.I fsanalyze
Xwill do the best it can to determine file system integrity by examining the
Xsuper block.
XIf the file system needs to be checked,
X.I fsanalyze
Xterminates with a message, unless overridden with the
X.B \-o
Xoption.  If a non-root file system is
Xmounted, a warning message is displayed, but analysis continues.  When
Xused on a mounted file system, it is recommended that a
X.I sync (1)
Xbe performed immediately prior to running
X.I fsanalyze .
XIf
X.I sync (1)
Xis not performed, the file system will not be damaged, but erroneous
Xstatistics may be reported.
X.SH OPTIONS
X.IP \fB\-b#\fR
Xassume '#' bytes per logical block -- by default, this value is calculated
Xautomatically.  Use this flag if the default value is in error.
X.IP \fB\-c#\fR
Xassume '#' sectors per disk cylinder -- by default, this value is
Xdetermined by information in the superblock.
X.IP \fB\-d\fR
Xdisplay inode numbers as they are examined.  This flag makes it easy to
Xchart the progress of fsanalyze through the file system.  Used mainly for
Xdebugging.
X.IP \fB\-e\fR
Xreport file size inconsistencies - the inode numbers are reported for files
Xwhere the file size and number of data blocks are inconsistent.  This
Xoption provides the same information as
X.I fsck (1)
Xphase 1.
X.IP \fB\-g#\fR
Xassume an inter-block gap of '#' sectors.  By default this information
Xis taken from the superblock.
X.IP \fB\-i\fR
Xreport double and triple indirection - the inode numbers are reported for
Xfiles that contain double and/or triple data-block indirection.
X.IP \fB\-o\fR
Xoverrides file system integrity checks.  The file system will be analyzed
Xeven if
X.I fsstat (1M)
Xreports that it is damaged.  Note that
X.I fsanalyze
Xmay give erroneous results if used on a damaged file system, but the file
Xsystem itself will not be affected.
X.IP \fB\-v\fR
Xcauses the current version number and patch level to be displayed.
X.SH EXAMPLES
Xfsanalyze /dev/dsk/0s2    # analyze file system
X.br
Xfsanalyze /dev/dsk/0s2 *  # analyze files in current dir
X.SH FILES
X/usr/include/sys/filesys.h	super block structure
X.br
X/usr/include/sys/ino.h		disk inode structure
X.br
X/usr/include/sys/param.h	system parameters
X.br
X/usr/include/sys/types.h	system type definitions
X.SH BUGS
XI don't trust the rotation delay statistics, especially for BSD file systems.
X.SH "SEE ALSO"
Xfsstat(1M), fsck(1M), fsdb(1M)
X.SH AUTHOR
XMichael J. Young
X.br
Xharvard!sdti!mjy
X.br
XInternet : mjy@sdti.SDTI.COM
X.SH VERSION
X4.1 \- 88/11/16 17:29:54
END_OF_fsanalyze.8
if test 7656 -ne `wc -c <fsanalyze.8`; then
    echo shar: \"fsanalyze.8\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f fsanalyze.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"fsanalyze.h\"
else
echo shar: Extracting \"fsanalyze.h\" \(9130 characters\)
sed "s/^X//" >fsanalyze.h <<'END_OF_fsanalyze.h'
X/* @(#)$Id: fsanalyze.h, V4.1 88/11/16 17:30:22 $ */
X
X/*
X * fsanalyze.h - file system analyzer header file
X * Version  : 4.1 - 88/11/16 17:30:22
X *
X * Author   : Michael J. Young
X * USmail   : Software Development Technologies, Inc.
X *            375 Dutton Rd
X *            Sudbury MA 01776
X * UUCP     : harvard!sdti!mjy
X * Internet : mjy@sdti.SDTI.COM
X *
X * =========================================================================
X * Note : This program has been placed in the public domain to permit
X * unrestricted distribution and use.  I have placed no copyright on it, but
X * I request that you keep me informed about any enhancements and bug fixes
X * you make so I can keep an up-to-date copy for further distribution.
X * =========================================================================
X */
X
X/*
X * Modification History:
X *
X *    Date       Author                   Description
X * -----------  --------  -----------------------------------------------
X * Wed Mar  9 17:51:38 EST 1988 - M. Young (mjy@sdti.SDTI.COM)
X *    Originated.
X *
X * Wed Apr 13 17:19:59 EDT 1988 - M. Young (mjy@sdti.SDTI.COM)
X *    Added support for BSD sockets to IS_SPECIAL macro
X *
X * Tue Jul 26 15:21:44 EDT 1988 - M. Young (mjy@sdti.SDTI.COM)
X *    Added wasted space metric
X *
X * Thu Jul 28 16:18:18 EDT 1988 - M. Young (mjy@sdti.SDTI.COM)
X *    Added external declarations due to code re-organization
X *
X * Mon Aug 08 11:27:39 EDT 1988 - M. Young (mjy@sdti.SDTI.COM),
X *    Revised OS_TYPE and FS_TYPE macros to avoid name-space conflicts
X *
X * Wed Nov 16 11:31:32 EST 1988 - M. Young (mjy@sdti.SDTI.COM),
X *    Placed under SCCS
X */
X
X#include <sys/param.h>
X
X#if (FS_TYPE == FS_ATT)
X# include <sys/types.h>
X# include <sys/filsys.h>
X# include <sys/ino.h>
X#endif
X
X#if (FS_TYPE == FS_BSD)
X# include <sys/fs.h>
X# include <sys/inode.h>
X#endif
X
X#if (FS_TYPE == FS_BSD_NFS)
X# include <sys/time.h>
X# include <ufs/fs.h>
X# include <sys/vnode.h>
X# include <ufs/inode.h>
X#endif
X
X#include <sys/stat.h>
X#include <varargs.h>
X#include <stdio.h>
X
X/*
X * ROOTINO doesn't seem to be defined in System V Release 3, so define
X * it here if necessary.
X */
X
X#ifndef	ROOTINO
X# define ROOTINO	2
X#endif
X
X/***************************************************************************
X *                            Basic Definitions                            *
X ***************************************************************************/
X
X#define FALSE   0
X#define TRUE    1
X
Xtypedef int boolean;
X
X/*
X * absolute value of difference of two numbers
X */
X# define diff(l1,l2)   ((l1) >= (l2) ? (l1) - (l2) : (l2) - (l1))
X
X/***************************************************************************
X *                          file mode definitions                          *
X ***************************************************************************/
X
X#define FILE_TYPE(a)    ((a)->di_mode & S_IFMT)
X
X/*
X * defines for determining whether or not a given inode is a special file
X */
X
X/*
X * S_IFNAM for XENIX-style semaphore and shared memory devices
X * S_IFLNK for BSD style symbolic links
X */
X#ifdef  S_IFNAM
X# define IS_NAM(a) (((a) & S_IFMT) == S_IFNAM)
X#else
X# define IS_NAM(a) FALSE
X#endif	/* XENIX IFNAM devices */
X
X#ifdef  S_IFLNK
X# define IS_LNK(a) (((a) & S_IFMT) == S_IFLNK)
X#else
X# define IS_LNK(a) FALSE
X#endif /* BSD symlinks */
X
X#ifdef  S_IFSOCK
X# define IS_SOK(a) (((a) & S_IFMT) == S_IFSOCK)
X#else
X# define IS_SOK(a) FALSE
X#endif /* BSD sockets */
X
X#define IS_SPECIAL(a) \
X        ((((a) & S_IFMT) == S_IFBLK) || \
X        (((a) & S_IFMT) == S_IFCHR) || \
X        (((a) & S_IFMT) == S_IFIFO) || \
X        IS_NAM(a) || IS_LNK(a) || IS_SOK(a))
X
X/*
X * per-file statistics structure
X */
Xstruct file_data {
X    int inode;                          /* i-node number */
X    long total_blocks;                  /* total blocks in file (incl 
X					 * indirect blocks */
X    long data_blocks;                   /* total data blocks in file */
X    long potential_seeks;		/* number of potential seeks in file */
X    long seeks;                         /* actual seeks in file */
X    long rotates;                       /* number of blocks which will suffer
X                                         * from rotation delay */
X    float fragm;                        /* fragmentation (seeks/pot.seeks) */
X    long min_cost;                      /* minimum seek cost for file */
X    long cost;                          /* actual seek cost for file */
X    float rel_cost;                     /* average relative seek cost */
X    long sparse;                        /* sparse file detections */
X    long wasted;                        /* wasted space due to partial last
X                                         * block */
X    };
X
X/***************************************************************************
X *                           External Declarations                         *
X ***************************************************************************/
X
Xextern int errno;
X
Xextern FILE *fsys;                      /* file system under test */
Xextern char *special;                   /* file system device name */
X
Xextern superblk_t *fil_sys;             /* file system superblock */
X
X/*
X * file system static data
X */
Xextern int interleave;                  /* file system interleave
X                                         * in logical blocks */
Xextern int block_size;                  /* bytes per logical block */
Xextern int cyl_size;                    /* physical sectors per cylinder */
Xextern dev_t fs_device;                 /* device ID of file system */
X
X/*
X * calculated global statistics
X */
Xextern long blocks;                     /* running block count */
Xextern long ind_blks;                   /* blocks used for indirect blocks */
Xextern long free_inodes;                /* number of unused i-nodes */
Xextern long potential_seeks;            /* potential number of disk seeks
X                                         * during sequential access of a 
X                                         * file */
Xextern long seeks;                      /* actual number of disk seeks
X                                         * required during sequential access
X                                         * of a file */
Xextern long total_seek_distance;        /* actual distance of disk seeks
X                                         * required during sequential access
X                                         * of a file */
Xextern long rotates;                    /* number of disk rotation delays */
Xextern long indirects;                  /* number of files w/ more than
X                                         * 10 data blocks */
Xextern long double_indirects;           /* number of files w/ more than
X                                        /* one level of indirection */
Xextern long triple_indirects;           /* number of files w/ more than
X                                         * two levels of indirection */
Xextern int big_directories;             /* number of directories with
X                                         * indirection */
Xextern int num_directories;             /* number of directories */
Xextern int num_specials;                /* number of special files */
Xextern int linked_files;                /* number of multiply-linked files */
Xextern int sparse_files;                /* number of sparse files */
Xextern int size_errors;                 /* number of file size discrepancies */
Xextern long unuseable;                  /* unuseable bytes due to external
X                                         * fragmentation */
X
Xextern struct file_data file_log[];     /* worst offenders */
X
X
X/*
X * fsanalyze command-line flags
X */
Xextern boolean rpt_indirects;           /* command line option -- report
X                                         * files with indirect data blocks */
Xextern boolean rpt_errors;              /* command line option -- report
X                                         * file size discrepancies */
Xextern boolean debug;                   /* report i-node #s as they are
X                                         * examined */
Xextern boolean override;                /* override bad fs test */
X
X
X/***************************************************************************
X *                          Function Declarations                          *
X ***************************************************************************/
X
X/*
X * from fsanalyze.c
X */
Xextern void error();			/* generic error reporter */
X
X/*
X * from init.c
X */
Xextern int init();			/* system initialization */
X
X/*
X * from util.c
X */
Xextern daddr_t blk_no();		/* get block number from inode structure */
Xextern void get_inodes();		/* fetch inode(s) */
X
X/*
X * from fragm.c
X */
Xextern void test_fragmentation();	/* fragmentation analyzer */
X
X/*
X * from chkfile.c
X */
Xextern void chk_file();			/* scan inode */
Xextern void scan();			/* analyze entire file system */
Xextern void anal_file();		/* analyze single file */
X
X/*
X * from stats.c
X */
Xextern void init_stats();		/* initialize inode statistics */
Xextern void log_stats();		/* log inode/file system stats */
X
X/*
X * from report.c
X */
Xextern void print_report();		/* report fs stats */
END_OF_fsanalyze.h
if test 9130 -ne `wc -c <fsanalyze.h`; then
    echo shar: \"fsanalyze.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f init.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"init.c\"
else
echo shar: Extracting \"init.c\" \(11197 characters\)
sed "s/^X//" >init.c <<'END_OF_init.c'
Xstatic char sccsid[] = "@(#)$Id: init.c, V4.1 88/11/16 17:30:51 $";
X
X/*
X * init.c - initialization and usage code
X * Version  : 4.1 - 88/11/16 17:30:51
X *
X * Author   : Michael J. Young
X * USmail   : Software Development Technologies, Inc.
X *            375 Dutton Rd
X *            Sudbury MA 01776
X * UUCP     : harvard!sdti!mjy
X * Internet : mjy@sdti.SDTI.COM
X *
X * =========================================================================
X * Note : This program has been placed in the public domain to permit
X * unrestricted distribution and use.  I have placed no copyright on it, but
X * I request that you keep me informed about any enhancements and bug fixes
X * you make so I can keep an up-to-date copy for further distribution.
X *
X * This program is being provided "as is", with no warrantee as to safety or
X * accuracy of results.  Use at your own risk.
X * =========================================================================
X */
X
X/*
X * Modification History:
X *
X * Thu Jul 28 15:50:42 EDT 1988 - M. Young (mjy@sdti.SDTI.COM)
X *    Extracted from fsanalyze.c.
X *
X * Mon Aug 08 11:27:39 EDT 1988 - M. Young (mjy@sdti.SDTI.COM),
X *    Revised OS_TYPE and FS_TYPE macros to avoid name-space conflicts
X *
X * Wed Nov 16 11:31:32 EST 1988 - M. Young (mjy@sdti.SDTI.COM),
X *    Placed under SCCS
X */
X
X#include "fsconfig.h"
X#include "fsanalyze.h"
X#include "patchlevel.h"
X
Xstatic superblk_t super = {0};          /* holds file system super block */
Xsuperblk_t *fil_sys     = &super;       /* external handle to super block */
X
XFILE *fsys               = NULL;        /* file system under test */
Xchar *special            = NULL;        /* file system device name */
X
X/*
X * file system static data
X */
Xint interleave           = 0;           /* file system interleave
X                                         * in logical blocks */
Xint block_size           = 0;           /* bytes per logical block */
Xint cyl_size             = 0;           /* physical sectors per cylinder */
Xdev_t fs_device          = 0;           /* device ID of file system */
X
X/*
X * fsanalyze command-line flags
X */
Xboolean rpt_indirects = FALSE;          /* command line option -- report
X                                         * files with indirect data blocks */
Xboolean rpt_errors    = FALSE;          /* command line option -- report
X                                         * file size discrepancies */
Xboolean debug         = FALSE;          /* report i-node #s as they are
X                                         * examined */
Xboolean override      = FALSE;          /* override bad fs test */
X
X/*
X * version : prints out the current version of fsanalyze
X */
Xvoid version(){
X    printf ("\nFile System Analyzer\n");
X    printf ("Version : 4.1 - 88/11/16 17:30:51, Patch Level #%d\n", patch_level);
X    }
X
X/*
X * usage : prints out a short message describing command usage.  This
X * function does not return.
X */
Xvoid usage(){
X    version();
X    printf ("Usage   : fsanalyze [-[deio] [-b#] [-c#] [-g#] ] special [file] ...\n");
X    printf ("\nIf the [file] argument(s) are missing, the entire file system\n");
X    printf ("is scanned.  Otherwise, only the specified files are examined.\n");
X    printf ("Valid option are:\n\n");
X    printf ("\tb#\tassume '#' bytes per logical block;\n");
X    printf ("\t\toverrides default calculation from the superblock.\n");
X    printf ("\tc#\tassume '#' sectors per disk cylinder;\n");
X    printf ("\t\toverrides information contained in superblock.\n");
X    printf ("\td\tdisplay i-node numbers as they are examined\n");
X    printf ("\te\treport file size inconsistencies\n");
X    printf ("\tg#\tassume an inter-block gap of '#' sectors;\n");
X    printf ("\t\toverrides information contained in superblock.\n");
X    printf ("\ti\treport data block double and triple indirection\n");
X    printf ("\to\toverride error checking on file system argument\n");
X    printf ("\tv\tdisplay current version number and patch level\n");
X    exit(1);
X    }
X
X/*
X * chk_fs : opens the file system and reads the superblock, then checks
X * the file system to be sure it is not damaged.  If the file system is
X * damaged, the function displays a message, then returns FALSE.  Otherwise,
X * returns TRUE.
X */
Xboolean chk_fs (fs)
Xchar *fs;
X{
X#ifdef  HAVE_FSSTAT
X    char buffer[256];                /* buffer to hold cmd */
X    int fstatus;                     /* exit status of fsstat */
X#endif  /* HAVE_FSSTAT */
X
X    extern int fstat();
X    struct stat sbuf;                /* buffer to hold file status */
X
X    /*
X     * make sure the file is a block structured device, then read
X     * the superblock
X     */
X    if ((fsys=fopen (fs, "r")) == NULL){
X        error (errno, "error opening \"%s\"\n", fs);
X        /* NOTREACHED */
X        }
X    if (fstat (fileno (fsys), &sbuf)){
X        error (errno, "fstat failed: \"%s\"\n", fs);
X        /* NOTREACHED */
X        }
X    if ((sbuf.st_mode & S_IFMT) != S_IFBLK){
X        fprintf (stderr, "\"%s\" is not a block structured device\n",
X                 fs);
X        return (FALSE);
X        }
X
X    /*
X     * save device ID of file system for later use.
X     */
X    fs_device = sbuf.st_rdev;
X
X#ifdef  HAVE_FSSTAT
X    /*
X     * Check the integrity of the file system using fsstat(1).
X     */
X    sprintf (buffer, "/etc/fsstat %s 2>/dev/null", fs);
X    fstatus = system (buffer);
X    if (fstatus != 0 && !override){
X        if (fstatus == 512){
X            fprintf (stderr, "warning: file system is mounted\n");
X            }
X        else {
X            /*
X             * run fsstat again to get error message (this is a kludge,
X             * but I hope more portable than having fsanalyze do the
X             * analysis itself)
X             */
X             sprintf (buffer, "/etc/fsstat %s", fs);
X             (void) system(buffer);
X             return (FALSE);
X             }
X         }
X#endif  /* HAVE_FSSTAT */
X
X    /*
X     * Read the file system super block
X     */
X    if (fseek (fsys, (long)SUPERBOFF, 0)){
X        error (errno, "error seeking superblock\n");
X        /* NOTREACHED */
X        }
X    if (fread (fil_sys, sizeof (superblk_t), 1, fsys) != 1){
X        error (errno, "error reading superblock\n");
X        /* NOTREACHED */
X        }
X
X#ifndef HAVE_FSSTAT
X    /*
X     * Check the integrity of the file system manually, if possible.
X     */
X    if (!is_ok(fil_sys)){
X        fprintf (stderr, "\"%s\" needs checking\n", fs);
X        return (FALSE);
X        }
X
X#endif  /* HAVE_FSSTAT */
X
X    return (TRUE);
X    }
X
X/*
X * init : parses command line flags and the file system device name, then
X * reads the file system super block.  init returns the number of the
X * parameter AFTER the file system device name.  All subsequent parameters
X * are assumed to be specific files to be tested.
X */
Xint init (argc, argv)
Xint argc;
Xchar *argv[];
X{
X    int i;                             /* loop counter */
X    char *cp;                          /* cmd-line flag pointer */
X    boolean special_found = FALSE;     /* TRUE = found special arg */
X    boolean done;                      /* means of escaping while loop
X                                        * from within a switch */
X    
X    for (i = 1; i < argc && !special_found; i++){
X        cp = argv[i];
X        if (*cp == '-'){
X            done = FALSE;
X            while (*++cp && !done){
X                switch (*cp){
X
X                        /* override logical block size */
X                    case 'b':
X                        if (cp[1]){
X                            block_size = atoi (++cp);
X                            done = TRUE;      /* force end of while loop */
X                            }
X                        else {
X                            block_size = atoi (argv[++i]);
X                            }
X                        if (block_size < 1){
X                            fprintf (stderr, "Illegal value for block size : %d, using default value of 512 bytes\n", block_size);
X                            block_size = 0;
X                            }
X                        break;
X
X                        /* override number of sectors per cylinder */
X                    case 'c':
X                        if (cp[1]){
X                            cyl_size = atoi (++cp);
X                            done = TRUE;      /* force end of while loop */
X                            }
X                        else {
X                            cyl_size = atoi (argv[++i]);
X                            }
X                        if (cyl_size < 1){
X                            fprintf (stderr, "Illegal value for cylinder size : %d\n", cyl_size);
X                            fprintf (stderr, "using default value from superblock\n");
X                            cyl_size = 0;
X                            }
X                        break;
X
X                        /* debugging flag */
X                    case 'd':
X                        debug = TRUE;
X                        break;
X
X                        /* report file size errors */
X                    case 'e':
X                        rpt_errors = TRUE;
X                        break;
X
X                        /* override inter-record gap */
X                    case 'g':
X                        if (cp[1]){
X                            interleave = atoi (++cp);
X                            done = TRUE;      /* force end of while loop */
X                            }
X                        else {
X                            interleave = atoi (argv[++i]);
X                            }
X                        if (interleave < 1){
X                            fprintf (stderr, "Illegal value for inter-record gap : %d\n", interleave);
X                            fprintf (stderr, "Using default value from superblock\n");
X                            interleave = 0;
X                            }
X                        break;
X
X                        /* report i-nodes with
X                         * one or more levels of
X                         * indirection */
X                    case 'i':
X                        rpt_indirects = TRUE;
X                        break;
X
X                        /* override bad fs test */
X                    case 'o':
X                        override = TRUE;
X                        break;
X
X                    case 'v':
X                        version();
X			exit(0);
X
X                    default:
X                        fprintf (stderr, "illegal option : %c\n", *cp);
X                        usage();
X                    }
X                }
X            }
X        else {
X            special = argv[i];
X            special_found = TRUE;
X            }
X        }
X    if (special == NULL)usage();                /* no fs parameter */
X
X    /*
X     * open file and make sure we have a good file system
X     */
X    if (!chk_fs (special))
X        exit(1);                /* bad file system, error status */
X
X    /*
X     * Set the default block size, if not already set.
X     */
X    if (block_size == 0){
X        block_size = fsize(fil_sys);
X        }
X
X    if (interleave == 0){
X        interleave = opt_interleave(fil_sys);
X        }
X
X    if (cyl_size == 0){
X       cyl_size = sec_per_cyl(fil_sys);
X       }
X
X    return i;                /* return # of next argument to be processed */
X    }
X
END_OF_init.c
if test 11197 -ne `wc -c <init.c`; then
    echo shar: \"init.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f report.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"report.c\"
else
echo shar: Extracting \"report.c\" \(7805 characters\)
sed "s/^X//" >report.c <<'END_OF_report.c'
Xstatic char sccsid[] = "@(#)$Id: report.c, V4.1 88/11/16 17:31:13 $";
X
X/*
X * report.c - print analysis report
X * Version  : 4.1 - 88/11/16 17:31:13
X *
X * Author   : Michael J. Young
X * USmail   : Software Development Technologies, Inc.
X *            375 Dutton Rd
X *            Sudbury MA 01776
X * UUCP     : harvard!sdti!mjy
X * Internet : mjy@sdti.SDTI.COM
X *
X * =========================================================================
X * Note : This program has been placed in the public domain to permit
X * unrestricted distribution and use.  I have placed no copyright on it, but
X * I request that you keep me informed about any enhancements and bug fixes
X * you make so I can keep an up-to-date copy for further distribution.
X *
X * This program is being provided "as is", with no warrantee as to safety or
X * accuracy of results.  Use at your own risk.
X * =========================================================================
X */
X
X/*
X * Modification History:
X *
X * Thu Jul 28 16:03:04 EDT 1988 - M. Young (mjy@sdti.SDTI.COM),
X *    Extracted from fsanalyze.c
X *
X * Mon Aug 08 11:27:39 EDT 1988 - M. Young (mjy@sdti.SDTI.COM),
X *    Revised OS_TYPE and FS_TYPE macros to avoid name-space conflicts
X *
X * Wed Nov 16 11:31:32 EST 1988 - M. Young (mjy@sdti.SDTI.COM),
X *    Placed under SCCS
X */
X
X#include "fsconfig.h"
X#include "fsanalyze.h"
X
X/*
X * print_report : calculates percentages and prints a summary report of
X * file system statistics
X */
Xvoid print_report (){
X    char    minibuf[32];          /* line buffer */
X    long    num_files;            /* number of inodes used
X                                   * in file system */
X    int     i;                    /* loop counter */
X    
X    float   fragm,                /* percent fragmentation */
X            rel_cost,             /* relative cost of fragmentation */
X            rotations,            /* percent rotation delays */
X            ind_p,                /* percent indirections */
X            dind_p,               /* percent double indirects */
X            tind_p,               /* percent triple indirects */
X            bused_p,              /* percent data blocks used */
X            iused_p,              /* percent inodes used */
X            sparse_p,             /* percent sparse inodes */
X            link_p,               /* percent multiply-linked
X                                   * files */
X            spec_p,               /* percent special files */
X            dir_p,                /* percent of inodes that
X                                   * are directories */
X            bdir_p,               /* percent of directories
X                                   * that have more than 10
X                                   * blocks */
X            indirb_p,             /* percent of blocks used for indirection */
X            wasted_p;             /* wasted space due to external frag */
X
X    /*
X     * calculate percentages for report
X     */
X    fragm = potential_seeks ? (float)seeks/(float)potential_seeks : 0.0;
X    rel_cost = seeks ? (float)total_seek_distance / (float)seeks : 0.0;
X    rotations = potential_seeks ? (float)rotates/(float)potential_seeks : 0.0;
X    bused_p = (float)blocks/(float)(blocks+freespace(fil_sys,0));
X    num_files = num_inodes(fil_sys) - free_inodes;
X    iused_p = (float)num_files/(float)num_inodes(fil_sys);
X    ind_p = num_files ? (float)indirects/(float)num_files : 0.0;
X    dind_p = num_files ? (float)double_indirects/(float)num_files : 0.0;
X    tind_p = num_files ? (float)triple_indirects/(float)num_files : 0.0;
X    sparse_p = num_files ? (float)sparse_files/(float)num_files : 0.0;
X    dir_p = num_files ? (float)num_directories/(float)num_files : 0.0;
X    bdir_p = num_directories ? (float)big_directories/(float)num_directories : 0.0;
X    link_p = num_files ? (float)linked_files/(float)num_files : 0.0;
X    spec_p = num_files ? (float)num_specials/(float)num_files : 0.0;
X    indirb_p = (float)ind_blks/(float)(blocks+freespace(fil_sys,0));
X    wasted_p = (float)unuseable/(float)(fssize(fil_sys) * block_size);
X
X    /*
X     * print out report
X     */
X# if FS_TYPE == FS_ATT
X    printf ("\n\nFile system name = \"%.6s\", Volume name = \"%.6s\"\n",
X        fil_sys->s_fname, fil_sys->s_fpack);
X# endif
X
X# if FS_TYPE == FS_BSD
X    /* Where do you get file system/volume name from? */
X# endif
X
X    printf ("Logical block size = %d bytes", bsize(fil_sys));
X
X# if FS_TYPE == FS_BSD
X    printf (", Fragment size = %d bytes", block_size);
X# endif
X
X    printf ("\nInterleave = %d sectors; %d sectors/cyl", 
X        interleave, cyl_size);
X
X# if FS_TYPE == FS_BSD
X    printf ("; Allocation Strategy = %s", 
X        fil_sys->fs_optim == FS_OPTTIME ? "Time" : "Space");
X# endif
X
X    printf ("\nVolume Size = %ld blocks (%ld bytes)\n",
X        fssize(fil_sys), fssize(fil_sys) * block_size);
X    printf ("\t%u blocks reserved for super block and inodes (%d inodes)\n", isize (fil_sys), num_inodes (fil_sys));
X    printf ("\t%lu blocks reserved for data\n", 
X        dsize(fil_sys));
X
X# if FS_TYPE == FS_BSD
X    printf ("Cylinder groups = %ld; Inodes per cg = %ld; Data Blocks per cg = %ld\n",
X            fil_sys->fs_ncg, fil_sys->fs_ipg, fil_sys->fs_fpg);
X# endif
X
X    printf ("%.2f%% inodes used (%ld used, %ld free)\n", 
X        iused_p*100, num_inodes(fil_sys) - free_inodes, free_inodes);
X    printf ("%.2f%% data blocks used (%ld used, %ld free)\n",
X        bused_p*100, blocks, freespace(fil_sys,0));
X
X    printf ("\n");
X    sprintf (minibuf, "%.2f%%", fragm*100);
X    printf ("Fragmentation         = %-15s", minibuf);
X/*    printf ("   (%ld seeks of %ld potential)\n", seeks, potential_seeks); */
X    printf ("  Special files    = %d (%.2f%%)\n",
X        num_specials, spec_p*100);
X    sprintf (minibuf, "%.2f cyls", rel_cost);
X    printf ("Average Seek Distance = %-15s", minibuf);
X    printf ("  Indirect files   = %ld (%.2f%%)\n",
X        indirects, ind_p*100);
X    sprintf (minibuf, "%.2f%%", rotations*100);
X    printf ("Rotation delays       = %-15s", minibuf);
X    printf ("  Double indirects = %ld (%.2f%%)\n",
X        double_indirects, dind_p*100);
X    sprintf (minibuf, "%d (%.2f%%)", linked_files, link_p*100);
X    printf ("Multiply-linked files = %-15s", minibuf);
X    printf ("  Triple indirects = %ld (%.2f%%)\n",
X        triple_indirects, tind_p*100);
X    sprintf (minibuf, "%d (%.2f%%)", num_directories, dir_p*100);
X    printf ("Directories           = %-15s", minibuf);
X    printf ("  Indirection blks = %ld (%.2f%%)\n",
X        ind_blks, indirb_p*100);
X    sprintf (minibuf, "%d (%.2f%%)", big_directories, bdir_p*100);
X    printf ("Oversized directories = %-15s", minibuf);
X    printf ("  Sparse files     = %d (%.2f%%)\n",
X        sparse_files, sparse_p*100);
X    printf ("Unused bytes in last blocks = %ld (%.2f%%)\n",
X        unuseable, wasted_p*100);
X    printf ("                            %d Most Fragmented Files\n",NUMOFFEND);
X    printf (" i-node  Size  Fragments   %%      Dist  i-node  Size  Fragments   %%       Dist\n");
X    for (i = 0; i < NUMOFFEND/2; i++){
X        if (file_log[i].inode != 0){
X            printf ("%6d %6ld   %6ld  %6.2f%% %6.1f",
X                file_log[i].inode,
X                file_log[i].total_blocks,
X                file_log[i].seeks+1,
X                file_log[i].fragm*100,
X                file_log[i].rel_cost);
X            if (file_log[i+(NUMOFFEND/2)].inode != 0){
X                printf (" %6d %6ld   %6ld  %6.2f%%  %6.1f",
X                    file_log[i+(NUMOFFEND/2)].inode,
X                    file_log[i+(NUMOFFEND/2)].total_blocks,
X                    file_log[i+(NUMOFFEND/2)].seeks+1,
X                    file_log[i+(NUMOFFEND/2)].fragm*100,
X                    file_log[i+(NUMOFFEND/2)].rel_cost);
X                }
X            printf ("\n");
X            }
X        else break;
X        }
X    }
X
END_OF_report.c
if test 7805 -ne `wc -c <report.c`; then
    echo shar: \"report.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mike Young
Software Development Technologies, Inc., Sudbury MA       Tel: +1 508 443 5779
Internet: mjy@sdti.sdti.com                 UUCP: {harvard,mit-eddie}!sdti!mjy