Whamcloud - gitweb
Fix gcc -Wall warnings, especially on 64-bit systems
[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 #define EXT4_EXTENTS_FL                 0x00080000 /* Inode uses extents */
48 #define EXT3_IOC_GETFLAGS               _IOR('f', 1, long)
49
50 static unsigned int div_ceil(unsigned int a, unsigned int b)
51 {
52         if (!a)
53                 return 0;
54         return ((a - 1) / b) + 1;
55 }
56
57 static unsigned long get_bmap(int fd, unsigned long block)
58 {
59         int     ret;
60         unsigned int b;
61
62         b = block;
63         ret = ioctl(fd, FIBMAP, &b); /* FIBMAP takes a pointer to an integer */
64         if (ret < 0) {
65                 if (errno == EPERM) {
66                         fprintf(stderr, "No permission to use FIBMAP ioctl; must have root privileges\n");
67                         exit(1);
68                 }
69                 perror("FIBMAP");
70         }
71         return b;
72 }
73
74 #define EXT2_DIRECT     12
75
76 static void frag_report(const char *filename)
77 {
78         struct statfs   fsinfo;
79 #ifdef HAVE_FSTAT64
80         struct stat64   fileinfo;
81 #else
82         struct stat     fileinfo;
83 #endif
84         int             bs;
85         long            fd;
86         unsigned long   block, last_block = 0, numblocks, i;
87         long            bpib;   /* Blocks per indirect block */
88         long            cylgroups;
89         int             discont = 0, expected;
90         int             is_ext2 = 0;
91         unsigned int    flags;
92
93         if (statfs(filename, &fsinfo) < 0) {
94                 perror("statfs");
95                 return;
96         }
97 #ifdef HAVE_FSTAT64
98         if (stat64(filename, &fileinfo) < 0) {
99 #else
100         if (stat(filename, &fileinfo) < 0) {
101 #endif
102                 perror("stat");
103                 return;
104         }
105         if (!S_ISREG(fileinfo.st_mode)) {
106                 printf("%s: Not a regular file\n", filename);
107                 return;
108         }
109         if ((fsinfo.f_type == 0xef51) || (fsinfo.f_type == 0xef52) || 
110             (fsinfo.f_type == 0xef53))
111                 is_ext2++;
112         if (verbose) {
113                 printf("Filesystem type is: %lx\n", 
114                        (unsigned long) fsinfo.f_type);
115         }
116         cylgroups = div_ceil(fsinfo.f_blocks, fsinfo.f_bsize*8);
117         if (verbose) {
118                 printf("Filesystem cylinder groups is approximately %ld\n", 
119                        cylgroups);
120         }
121 #ifdef HAVE_OPEN64
122         fd = open64(filename, O_RDONLY);
123 #else
124         fd = open(filename, O_RDONLY);
125 #endif
126         if (fd < 0) {
127                 perror("open");
128                 return;
129         }
130         if (ioctl(fd, FIGETBSZ, &bs) < 0) { /* FIGETBSZ takes an int */
131                 perror("FIGETBSZ");
132                 close(fd);
133                 return;
134         }
135         if (ioctl(fd, EXT3_IOC_GETFLAGS, &flags) < 0)
136                 flags = 0;
137         if (flags & EXT4_EXTENTS_FL) {
138                 printf("File is stored in extents format\n");
139                 is_ext2 = 0;
140         }
141         if (verbose)
142                 printf("Blocksize of file %s is %d\n", filename, bs);
143         bpib = bs / 4;
144         numblocks = (fileinfo.st_size + (bs-1)) / bs;
145         if (verbose) {
146                 printf("File size of %s is %lld (%ld blocks)\n", filename, 
147                        (long long) fileinfo.st_size, numblocks);
148                 printf("First block: %lu\nLast block: %lu\n",
149                        get_bmap(fd, 0), get_bmap(fd, numblocks - 1));
150         }
151         for (i=0; i < numblocks; i++) {
152                 if (is_ext2 && last_block) {
153                         if (((i-EXT2_DIRECT) % bpib) == 0)
154                                 last_block++;
155                         if (((i-EXT2_DIRECT-bpib) % (bpib*bpib)) == 0)
156                                 last_block++;
157                         if (((i-EXT2_DIRECT-bpib-bpib*bpib) % (bpib*bpib*bpib)) == 0)
158                                 last_block++;
159                 }
160                 block = get_bmap(fd, i);
161                 if (block == 0)
162                         continue;
163                 if (last_block && (block != last_block +1) ) {
164                         if (verbose)
165                                 printf("Discontinuity: Block %ld is at %lu (was %lu)\n",
166                                        i, block, last_block);
167                         discont++;
168                 }
169                 last_block = block;
170         }
171         if (discont==0)
172                 printf("%s: 1 extent found", filename);
173         else
174                 printf("%s: %d extents found", filename, discont+1);
175         expected = (numblocks/((bs*8)-(fsinfo.f_files/8/cylgroups)-3))+1;
176         if (is_ext2 && expected != discont+1)
177                 printf(", perfection would be %d extent%s\n", expected,
178                         (expected>1) ? "s" : "");
179         else
180                 fputc('\n', stdout);
181         close(fd);
182 }
183
184 static void usage(const char *progname)
185 {
186         fprintf(stderr, "Usage: %s [-v] file ...\n", progname);
187         exit(1);
188 }
189
190 int main(int argc, char**argv)
191 {
192         char **cpp;
193         int c;
194
195         while ((c = getopt(argc, argv, "v")) != EOF)
196                 switch (c) {
197                 case 'v':
198                         verbose++;
199                         break;
200                 default:
201                         usage(argv[0]);
202                         break;
203                 }
204         if (optind == argc)
205                 usage(argv[0]);
206         for (cpp=argv+optind; *cpp; cpp++) {
207                 if (verbose)
208                         printf("Checking %s\n", *cpp);
209                 frag_report(*cpp);
210         }
211         return 0;
212 }
213 #endif