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