312 lines
10 KiB
C
312 lines
10 KiB
C
/* frag.c - simple fragmentation checker
|
|
V1.0 by Werner Almesberger
|
|
V1.1 by Steffen Zahn, adding directory recursion
|
|
V1.2 by Rob Hooft, adding hole counts
|
|
V1.3 by Steffen Zahn, email: szahn%masterix@emndev.siemens.co.at
|
|
14 Nov 93
|
|
- ignore symlinks,
|
|
- don't cross filesys borders
|
|
- get filesystem block size at runtime
|
|
V1.4 by Michael Bischoff <mbi@mo.math.nat.tu-bs.de> to handle
|
|
indirect blocks better, but only for ext2fs
|
|
(applied by faith@cs.unc.edu, Sat Feb 4 22:06:27 1995)
|
|
|
|
TODO: - handle hard links
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <dirent.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/vfs.h>
|
|
#include <linux/fs.h> /* for FIBMAP */
|
|
|
|
typedef struct StackElem {
|
|
struct StackElem *backref, *next;
|
|
char name[NAME_MAX];
|
|
char dir_seen;
|
|
char from_cmd_line;
|
|
} StackElem;
|
|
|
|
StackElem *top = NULL;
|
|
|
|
|
|
void discard( void )
|
|
{
|
|
StackElem *se = top;
|
|
if( se == NULL )
|
|
return ;
|
|
top = se->next;
|
|
free(se);
|
|
}
|
|
|
|
void push( StackElem * se )
|
|
{
|
|
se -> next = top;
|
|
top = se;
|
|
}
|
|
|
|
char *p2s( StackElem *se, char *path )
|
|
{
|
|
char *s;
|
|
if( se->backref!=NULL ) {
|
|
path = p2s( se->backref, path );
|
|
if( path[-1]!='/' )
|
|
*path++ = '/';
|
|
}
|
|
s = se->name;
|
|
while( *s )
|
|
*path++ = *s++;
|
|
return path;
|
|
}
|
|
|
|
char *path2str( StackElem *se, char *path )
|
|
{
|
|
*(p2s( se, path ))=0;
|
|
return path;
|
|
}
|
|
|
|
void *xmalloc( size_t size )
|
|
{
|
|
void *p;
|
|
if( (p=malloc(size))==NULL ) {
|
|
fprintf(stderr,"\nvirtual memory exhausted.\n");
|
|
exit(1);
|
|
}
|
|
return p;
|
|
}
|
|
|
|
int main(int argc,char **argv)
|
|
{
|
|
int fd,last_phys_block,
|
|
fragments_in_file, blocks_in_file,
|
|
blocks,current_phys_block,
|
|
this_fragment, largest_fragment, i;
|
|
long sum_blocks=0, sum_frag_blocks=0, sum_files=0, sum_frag_files=0;
|
|
long num_hole=0, sum_hole=0, hole;
|
|
struct stat st;
|
|
struct statfs stfs;
|
|
StackElem *se, *se1;
|
|
char path[PATH_MAX], pathlink[PATH_MAX], *p;
|
|
DIR *dir;
|
|
struct dirent *de;
|
|
char silent_flag=0;
|
|
dev_t local_fs;
|
|
int block_size;
|
|
|
|
if (argc < 2)
|
|
{
|
|
fprintf(stderr,"usage: %s [-s [-s]] filename ...\n",argv[0]);
|
|
exit(1);
|
|
}
|
|
argc--; argv++;
|
|
while (argc>0)
|
|
{
|
|
p = *argv;
|
|
if( *p=='-' )
|
|
while( *++p )
|
|
switch( *p )
|
|
{
|
|
case 's':
|
|
silent_flag++; /* may be 1 or 2 */
|
|
break;
|
|
default:
|
|
fprintf(stderr,"\nunknown flag %c\n", *p );
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
se = xmalloc( sizeof(StackElem) );
|
|
se->backref=NULL; se->dir_seen=0; se->from_cmd_line=1;
|
|
strcpy( se->name, p );
|
|
push(se);
|
|
}
|
|
argc--; argv++;
|
|
}
|
|
while ( top != NULL)
|
|
{
|
|
se = top;
|
|
if( se->dir_seen )
|
|
discard();
|
|
else
|
|
{
|
|
path2str( se, path );
|
|
if( readlink( path, pathlink, sizeof(pathlink) )>=0 )
|
|
{ /* ignore symlinks */
|
|
if(silent_flag<1)
|
|
{
|
|
printf("symlink %s\n", path );
|
|
}
|
|
discard();
|
|
}
|
|
else if( stat( path,&st) < 0)
|
|
{
|
|
perror( path );
|
|
discard();
|
|
}
|
|
else if( !se->from_cmd_line && (local_fs!=st.st_dev) )
|
|
{ /* do not cross filesystem borders */
|
|
if(silent_flag<2)
|
|
{
|
|
printf("different filesystem %s\n", path );
|
|
}
|
|
discard();
|
|
}
|
|
else
|
|
{
|
|
if( se->from_cmd_line )
|
|
{
|
|
local_fs = st.st_dev;
|
|
if ( statfs( path, &stfs )<0 )
|
|
{
|
|
perror( path );
|
|
block_size = 1024;
|
|
}
|
|
else
|
|
block_size = stfs.f_bsize;
|
|
}
|
|
if( S_ISREG(st.st_mode)) /* regular file */
|
|
{
|
|
if ( (fd = open( path ,O_RDONLY)) < 0 )
|
|
{
|
|
perror( path );
|
|
discard();
|
|
}
|
|
else
|
|
{
|
|
last_phys_block = -1;
|
|
fragments_in_file = 0;
|
|
hole = 0; this_fragment=0;
|
|
largest_fragment=0;
|
|
blocks_in_file = (st.st_size+block_size-1)/block_size;
|
|
for (blocks = 0; blocks < blocks_in_file; blocks++)
|
|
{
|
|
current_phys_block = blocks;
|
|
if (ioctl(fd,FIBMAP,¤t_phys_block) < 0)
|
|
{
|
|
perror(path);
|
|
break;
|
|
}
|
|
if (current_phys_block) { /* no hole here */
|
|
int indirect;
|
|
/* indirect is the number of indirection */
|
|
/* blocks which must be skipped */
|
|
indirect = 0;
|
|
/* every 256 blocks there is an indirect block,
|
|
the first of these is before block 12 */
|
|
if (blocks >= 12 && (blocks-12) % 256 == 0)
|
|
++indirect;
|
|
/* there is a block pointing to the indirect
|
|
blocks every 64K blocks */
|
|
if (blocks >= 256+12 && (blocks-256-12) % 65536 == 0)
|
|
++indirect; /* 2nd indirect block */
|
|
/* there is a single triple indirect block */
|
|
if (blocks == 65536 + 256 + 12)
|
|
++indirect;
|
|
if (last_phys_block == current_phys_block-1-indirect)
|
|
this_fragment++;
|
|
else { /* start of first or new fragment */
|
|
if( largest_fragment<this_fragment )
|
|
largest_fragment=this_fragment;
|
|
this_fragment=1;
|
|
fragments_in_file++;
|
|
}
|
|
last_phys_block = current_phys_block;
|
|
}
|
|
else
|
|
{
|
|
hole++;
|
|
}
|
|
}
|
|
if( largest_fragment<this_fragment )
|
|
largest_fragment=this_fragment;
|
|
blocks_in_file-=hole;
|
|
/* number of allocated blocks in file */
|
|
if( !silent_flag )
|
|
{
|
|
if( fragments_in_file < 2
|
|
|| blocks_in_file < 2 )
|
|
i = 0; /* fragmentation 0 % */
|
|
else
|
|
i = (fragments_in_file - 1) * 100 /
|
|
(blocks_in_file-1);
|
|
/* maximum fragmentation 100%
|
|
means every block is an fragment */
|
|
printf(" %3d%% %s (%d block(s), %d fragment(s), largest %d",
|
|
i, path, blocks_in_file,
|
|
fragments_in_file,largest_fragment);
|
|
if (hole)
|
|
{
|
|
printf(", %d hole(s))\n",hole);
|
|
}
|
|
else
|
|
{
|
|
printf(")\n");
|
|
}
|
|
}
|
|
sum_blocks+=blocks_in_file;
|
|
if (hole)
|
|
num_hole++;
|
|
sum_hole+=hole;
|
|
sum_files++;
|
|
if( fragments_in_file>1 )
|
|
{
|
|
sum_frag_blocks+=blocks_in_file-largest_fragment;
|
|
sum_frag_files++;
|
|
}
|
|
discard();
|
|
close(fd);
|
|
}
|
|
}
|
|
else if( S_ISDIR( st.st_mode ) ) /* push dir contents */
|
|
{
|
|
if( (dir=opendir( path ))==NULL )
|
|
{
|
|
perror(path);
|
|
discard();
|
|
}
|
|
else
|
|
{
|
|
if( silent_flag<2 )
|
|
printf("reading %s\n", path);
|
|
while( (de=readdir(dir))!=NULL )
|
|
{
|
|
if( (strcmp(de->d_name,".")!=0)
|
|
&& (strcmp(de->d_name,"..")!=0) )
|
|
{
|
|
se1 = xmalloc( sizeof(StackElem) );
|
|
se1->backref=se; se1->dir_seen=0;
|
|
se1->from_cmd_line=0;
|
|
strcpy( se1->name, de->d_name );
|
|
push(se1);
|
|
}
|
|
}
|
|
closedir( dir );
|
|
se->dir_seen=1;
|
|
}
|
|
}
|
|
else /* if( S_ISREG(st.st_mode)) */
|
|
discard();
|
|
}
|
|
} /* if( se->dir_seen ) */
|
|
} /* while ( top != NULL) */
|
|
if (sum_files>1)
|
|
{
|
|
printf("\nsummary:\n");
|
|
printf(" %3ld%% file fragmentation (%ld of %ld files contain fragments)\n",
|
|
sum_files<1 ? 0L : sum_frag_files*100/sum_files,
|
|
sum_frag_files, sum_files);
|
|
printf(" %3ld%% block fragmentation (%ld of %ld blocks are in fragments)\n",
|
|
sum_blocks<1 ? 0L : sum_frag_blocks*100/sum_blocks,
|
|
sum_frag_blocks, sum_blocks);
|
|
if (num_hole>1)
|
|
printf(" %ld files contain %ld blocks in holes\n",
|
|
num_hole,sum_hole);
|
|
}
|
|
exit(0);
|
|
}
|