]> git.ipfire.org Git - thirdparty/util-linux.git/blob - historic/frag.c
Imported from util-linux-2.5 tarball.
[thirdparty/util-linux.git] / historic / frag.c
1 /* frag.c - simple fragmentation checker
2 V1.0 by Werner Almesberger
3 V1.1 by Steffen Zahn, adding directory recursion
4 V1.2 by Rob Hooft, adding hole counts
5 V1.3 by Steffen Zahn, email: szahn%masterix@emndev.siemens.co.at
6 14 Nov 93
7 - ignore symlinks,
8 - don't cross filesys borders
9 - get filesystem block size at runtime
10 V1.4 by Michael Bischoff <mbi@mo.math.nat.tu-bs.de> to handle
11 indirect blocks better, but only for ext2fs
12 (applied by faith@cs.unc.edu, Sat Feb 4 22:06:27 1995)
13
14 TODO: - handle hard links
15 */
16
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <limits.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <dirent.h>
24 #include <sys/stat.h>
25 #include <sys/vfs.h>
26 #include <linux/fs.h> /* for FIBMAP */
27
28 typedef struct StackElem {
29 struct StackElem *backref, *next;
30 char name[NAME_MAX];
31 char dir_seen;
32 char from_cmd_line;
33 } StackElem;
34
35 StackElem *top = NULL;
36
37
38 void discard( void )
39 {
40 StackElem *se = top;
41 if( se == NULL )
42 return ;
43 top = se->next;
44 free(se);
45 }
46
47 void push( StackElem * se )
48 {
49 se -> next = top;
50 top = se;
51 }
52
53 char *p2s( StackElem *se, char *path )
54 {
55 char *s;
56 if( se->backref!=NULL ) {
57 path = p2s( se->backref, path );
58 if( path[-1]!='/' )
59 *path++ = '/';
60 }
61 s = se->name;
62 while( *s )
63 *path++ = *s++;
64 return path;
65 }
66
67 char *path2str( StackElem *se, char *path )
68 {
69 *(p2s( se, path ))=0;
70 return path;
71 }
72
73 void *xmalloc( size_t size )
74 {
75 void *p;
76 if( (p=malloc(size))==NULL ) {
77 fprintf(stderr,"\nvirtual memory exhausted.\n");
78 exit(1);
79 }
80 return p;
81 }
82
83 int main(int argc,char **argv)
84 {
85 int fd,last_phys_block,
86 fragments_in_file, blocks_in_file,
87 blocks,current_phys_block,
88 this_fragment, largest_fragment, i;
89 long sum_blocks=0, sum_frag_blocks=0, sum_files=0, sum_frag_files=0;
90 long num_hole=0, sum_hole=0, hole;
91 struct stat st;
92 struct statfs stfs;
93 StackElem *se, *se1;
94 char path[PATH_MAX], pathlink[PATH_MAX], *p;
95 DIR *dir;
96 struct dirent *de;
97 char silent_flag=0;
98 dev_t local_fs;
99 int block_size;
100
101 if (argc < 2)
102 {
103 fprintf(stderr,"usage: %s [-s [-s]] filename ...\n",argv[0]);
104 exit(1);
105 }
106 argc--; argv++;
107 while (argc>0)
108 {
109 p = *argv;
110 if( *p=='-' )
111 while( *++p )
112 switch( *p )
113 {
114 case 's':
115 silent_flag++; /* may be 1 or 2 */
116 break;
117 default:
118 fprintf(stderr,"\nunknown flag %c\n", *p );
119 exit(1);
120 }
121 else
122 {
123 se = xmalloc( sizeof(StackElem) );
124 se->backref=NULL; se->dir_seen=0; se->from_cmd_line=1;
125 strcpy( se->name, p );
126 push(se);
127 }
128 argc--; argv++;
129 }
130 while ( top != NULL)
131 {
132 se = top;
133 if( se->dir_seen )
134 discard();
135 else
136 {
137 path2str( se, path );
138 if( readlink( path, pathlink, sizeof(pathlink) )>=0 )
139 { /* ignore symlinks */
140 if(silent_flag<1)
141 {
142 printf("symlink %s\n", path );
143 }
144 discard();
145 }
146 else if( stat( path,&st) < 0)
147 {
148 perror( path );
149 discard();
150 }
151 else if( !se->from_cmd_line && (local_fs!=st.st_dev) )
152 { /* do not cross filesystem borders */
153 if(silent_flag<2)
154 {
155 printf("different filesystem %s\n", path );
156 }
157 discard();
158 }
159 else
160 {
161 if( se->from_cmd_line )
162 {
163 local_fs = st.st_dev;
164 if ( statfs( path, &stfs )<0 )
165 {
166 perror( path );
167 block_size = 1024;
168 }
169 else
170 block_size = stfs.f_bsize;
171 }
172 if( S_ISREG(st.st_mode)) /* regular file */
173 {
174 if ( (fd = open( path ,O_RDONLY)) < 0 )
175 {
176 perror( path );
177 discard();
178 }
179 else
180 {
181 last_phys_block = -1;
182 fragments_in_file = 0;
183 hole = 0; this_fragment=0;
184 largest_fragment=0;
185 blocks_in_file = (st.st_size+block_size-1)/block_size;
186 for (blocks = 0; blocks < blocks_in_file; blocks++)
187 {
188 current_phys_block = blocks;
189 if (ioctl(fd,FIBMAP,&current_phys_block) < 0)
190 {
191 perror(path);
192 break;
193 }
194 if (current_phys_block) { /* no hole here */
195 int indirect;
196 /* indirect is the number of indirection */
197 /* blocks which must be skipped */
198 indirect = 0;
199 /* every 256 blocks there is an indirect block,
200 the first of these is before block 12 */
201 if (blocks >= 12 && (blocks-12) % 256 == 0)
202 ++indirect;
203 /* there is a block pointing to the indirect
204 blocks every 64K blocks */
205 if (blocks >= 256+12 && (blocks-256-12) % 65536 == 0)
206 ++indirect; /* 2nd indirect block */
207 /* there is a single triple indirect block */
208 if (blocks == 65536 + 256 + 12)
209 ++indirect;
210 if (last_phys_block == current_phys_block-1-indirect)
211 this_fragment++;
212 else { /* start of first or new fragment */
213 if( largest_fragment<this_fragment )
214 largest_fragment=this_fragment;
215 this_fragment=1;
216 fragments_in_file++;
217 }
218 last_phys_block = current_phys_block;
219 }
220 else
221 {
222 hole++;
223 }
224 }
225 if( largest_fragment<this_fragment )
226 largest_fragment=this_fragment;
227 blocks_in_file-=hole;
228 /* number of allocated blocks in file */
229 if( !silent_flag )
230 {
231 if( fragments_in_file < 2
232 || blocks_in_file < 2 )
233 i = 0; /* fragmentation 0 % */
234 else
235 i = (fragments_in_file - 1) * 100 /
236 (blocks_in_file-1);
237 /* maximum fragmentation 100%
238 means every block is an fragment */
239 printf(" %3d%% %s (%d block(s), %d fragment(s), largest %d",
240 i, path, blocks_in_file,
241 fragments_in_file,largest_fragment);
242 if (hole)
243 {
244 printf(", %d hole(s))\n",hole);
245 }
246 else
247 {
248 printf(")\n");
249 }
250 }
251 sum_blocks+=blocks_in_file;
252 if (hole)
253 num_hole++;
254 sum_hole+=hole;
255 sum_files++;
256 if( fragments_in_file>1 )
257 {
258 sum_frag_blocks+=blocks_in_file-largest_fragment;
259 sum_frag_files++;
260 }
261 discard();
262 close(fd);
263 }
264 }
265 else if( S_ISDIR( st.st_mode ) ) /* push dir contents */
266 {
267 if( (dir=opendir( path ))==NULL )
268 {
269 perror(path);
270 discard();
271 }
272 else
273 {
274 if( silent_flag<2 )
275 printf("reading %s\n", path);
276 while( (de=readdir(dir))!=NULL )
277 {
278 if( (strcmp(de->d_name,".")!=0)
279 && (strcmp(de->d_name,"..")!=0) )
280 {
281 se1 = xmalloc( sizeof(StackElem) );
282 se1->backref=se; se1->dir_seen=0;
283 se1->from_cmd_line=0;
284 strcpy( se1->name, de->d_name );
285 push(se1);
286 }
287 }
288 closedir( dir );
289 se->dir_seen=1;
290 }
291 }
292 else /* if( S_ISREG(st.st_mode)) */
293 discard();
294 }
295 } /* if( se->dir_seen ) */
296 } /* while ( top != NULL) */
297 if (sum_files>1)
298 {
299 printf("\nsummary:\n");
300 printf(" %3ld%% file fragmentation (%ld of %ld files contain fragments)\n",
301 sum_files<1 ? 0L : sum_frag_files*100/sum_files,
302 sum_frag_files, sum_files);
303 printf(" %3ld%% block fragmentation (%ld of %ld blocks are in fragments)\n",
304 sum_blocks<1 ? 0L : sum_frag_blocks*100/sum_blocks,
305 sum_frag_blocks, sum_blocks);
306 if (num_hole>1)
307 printf(" %ld files contain %ld blocks in holes\n",
308 num_hole,sum_hole);
309 }
310 exit(0);
311 }