/* 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 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 #include #include #include #include #include #include #include #include #include /* 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_fragment1 ) { 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); }