Whamcloud - gitweb
filefrag.c (frag_report): In verbose mode, print the first and
[tools/e2fsprogs.git] / misc / filefrag.c
1 /*
2  * filefrag.c -- report if a particular file is fragmented
3  * 
4  * Copyright 2003 by Theodore Ts'o.
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the GNU Public
8  * License.
9  * %End-Header%
10  */
11
12 #ifndef __linux__
13 #include <stdio.h>
14 #include <stdlib.h>
15
16 int main(void) {
17     fputs("This program is only supported on Linux!\n", stderr);
18     exit(EXIT_FAILURE);
19 }
20 #else
21 #define _LARGEFILE64_SOURCE
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <time.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #ifdef HAVE_GETOPT_H
31 #include <getopt.h>
32 #else
33 extern char *optarg;
34 extern int optind;
35 #endif
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/vfs.h>
39 #include <sys/ioctl.h>
40 #include <linux/fd.h>
41
42 int verbose = 0;
43
44 #define FIBMAP     _IO(0x00,1)  /* bmap access */
45 #define FIGETBSZ   _IO(0x00,2)  /* get the block size used for bmap */
46
47 static unsigned long get_bmap(int fd, unsigned long block)
48 {
49         int     ret;
50         unsigned long b;
51
52         b = block;
53         ret = ioctl(fd, FIBMAP, &b);
54         if (ret < 0) {
55                 if (errno == EPERM) {
56                         fprintf(stderr, "No permission to use FIBMAP ioctl; must have root privileges\n");
57                         exit(1);
58                 }
59                 perror("FIBMAP");
60         }
61         return b;
62 }
63
64 #define EXT2_DIRECT     12
65
66 static void frag_report(const char *filename)
67 {
68         struct statfs   fsinfo;
69         struct stat64   fileinfo;
70         long            i, fd, bs, block, last_block = 0, numblocks;
71         long            bpib;   /* Blocks per indirect block */
72         long            cylgroups;
73         int             discont = 0, expected;
74         int             is_ext2 = 0;
75
76         if (statfs(filename, &fsinfo) < 0) {
77                 perror("statfs");
78                 return;
79         }
80         if (stat64(filename, &fileinfo) < 0) {
81                 perror("stat");
82                 return;
83         }
84         if (!S_ISREG(fileinfo.st_mode)) {
85                 printf("%s: Not a regular file\n", filename);
86                 return;
87         }
88         if ((fsinfo.f_type == 0xef51) || (fsinfo.f_type == 0xef52) || 
89             (fsinfo.f_type == 0xef53))
90                 is_ext2++;
91         if (verbose) {
92                 printf("Filesystem type is: %x\n", fsinfo.f_type);
93         }
94         cylgroups = (fsinfo.f_blocks + fsinfo.f_bsize*8-1) / fsinfo.f_bsize*8;
95         if (verbose) {
96                 printf("Filesystem cylinder groups is approximately %ld\n", 
97                        cylgroups);
98         }
99         fd = open(filename, O_RDONLY | O_LARGEFILE);
100         if (fd < 0) {
101                 perror("open");
102                 return;
103         }
104         if (ioctl(fd, FIGETBSZ, &bs) < 0) {
105                 perror("FIGETBSZ");
106                 close(fd);
107                 return;
108         }
109         if (verbose)
110                 printf("Blocksize of file %s is %ld\n", filename, bs);
111         bpib = bs / 4;
112         numblocks = (fileinfo.st_size + (bs-1)) / bs;
113         if (verbose) {
114                 printf("File size of %s is %lld (%ld blocks)\n", filename, 
115                        (long long) fileinfo.st_size, numblocks);
116                 printf("First block: %ld\nLast block: %ld\n",
117                        get_bmap(fd, 0), get_bmap(fd, numblocks - 1));
118         }
119         for (i=0; i < numblocks; i++) {
120                 if (is_ext2) {
121                         if (((i-EXT2_DIRECT) % bpib) == 0)
122                                 last_block++;
123                         if (((i-EXT2_DIRECT-bpib) % (bpib*bpib)) == 0)
124                                 last_block++;
125                         if (((i-EXT2_DIRECT-bpib-bpib*bpib) % (bpib*bpib*bpib)) == 0)
126                                 last_block++;
127                 }
128                 block = get_bmap(fd, i);
129                 if (i && block && (block != last_block +1) ) {
130                         if (verbose)
131                                 printf("Discontinuity: Block %ld is at %ld (was %ld)\n",
132                                        i, block, last_block);
133                         discont++;
134                 }
135                 if (block)
136                         last_block = block;
137         }
138         if (discont==0)
139                 printf("%s: 1 extent found", filename);
140         else
141                 printf("%s: %d extents found", filename, discont+1);
142         expected = (numblocks/((bs*8)-(fsinfo.f_files/8/cylgroups)-3))+1;
143         if (is_ext2 && expected != discont+1)
144                 printf(", perfection would be %d extent%s\n", expected,
145                         (expected>1) ? "s" : "");
146         else
147                 fputc('\n', stdout);
148         close(fd);
149 }
150
151 static void usage(const char *progname)
152 {
153         fprintf(stderr, "Usage: %s [-v] file ...\n", progname);
154         exit(1);
155 }
156
157 int main(int argc, char**argv)
158 {
159         char **cpp;
160         int c;
161
162         while ((c = getopt(argc, argv, "v")) != EOF)
163                 switch (c) {
164                 case 'v':
165                         verbose++;
166                         break;
167                 default:
168                         usage(argv[0]);
169                         break;
170                 }
171         if (optind == argc)
172                 usage(argv[0]);
173         for (cpp=argv+optind; *cpp; cpp++) {
174                 if (verbose)
175                         printf("Checking %s\n", *cpp);
176                 frag_report(*cpp);
177         }
178         return 0;
179 }
180 #endif