OSDN Git Service

merge in master-release history after reset to 2079d768f79f636223d89b988a30209adf8dddbe
[android-x86/external-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 #include <unistd.h>
16
17 int main(void) {
18         fputs("This program is only supported on Linux!\n", stderr);
19         exit(EXIT_FAILURE);
20 }
21 #else
22 #define _LARGEFILE64_SOURCE
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <time.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #ifdef HAVE_GETOPT_H
32 #include <getopt.h>
33 #else
34 extern char *optarg;
35 extern int optind;
36 #endif
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/vfs.h>
40 #include <sys/ioctl.h>
41 #include <linux/fd.h>
42 #include <ext2fs/ext2fs.h>
43 #include <ext2fs/ext2_types.h>
44 #include <ext2fs/fiemap.h>
45
46 int verbose = 0;
47 int blocksize;          /* Use specified blocksize (default 1kB) */
48 int sync_file = 0;      /* fsync file before getting the mapping */
49 int xattr_map = 0;      /* get xattr mapping */
50 int force_bmap; /* force use of FIBMAP instead of FIEMAP */
51 int force_extent;       /* print output in extent format always */
52 int logical_width = 8;
53 int physical_width = 10;
54 const char *ext_fmt = "%4d: %*llu..%*llu: %*llu..%*llu: %6llu: %s\n";
55 const char *hex_fmt = "%4d: %*llx..%*llx: %*llx..%*llx: %6llx: %s\n";
56
57 #define FILEFRAG_FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
58
59 #define FIBMAP          _IO(0x00, 1)    /* bmap access */
60 #define FIGETBSZ        _IO(0x00, 2)    /* get the block size used for bmap */
61
62 #define LUSTRE_SUPER_MAGIC 0x0BD00BD0
63
64 #define EXT4_EXTENTS_FL                 0x00080000 /* Inode uses extents */
65 #define EXT3_IOC_GETFLAGS               _IOR('f', 1, long)
66
67 static int int_log2(int arg)
68 {
69         int     l = 0;
70
71         arg >>= 1;
72         while (arg) {
73                 l++;
74                 arg >>= 1;
75         }
76         return l;
77 }
78
79 static int int_log10(unsigned long long arg)
80 {
81         int     l = 0;
82
83         arg = arg / 10;
84         while (arg) {
85                 l++;
86                 arg = arg / 10;
87         }
88         return l;
89 }
90
91 static unsigned int div_ceil(unsigned int a, unsigned int b)
92 {
93         if (!a)
94                 return 0;
95         return ((a - 1) / b) + 1;
96 }
97
98 static int get_bmap(int fd, unsigned long block, unsigned long *phy_blk)
99 {
100         int     ret;
101         unsigned int b;
102
103         b = block;
104         ret = ioctl(fd, FIBMAP, &b); /* FIBMAP takes pointer to integer */
105         if (ret < 0) {
106                 if (errno == EPERM) {
107                         fprintf(stderr, "No permission to use FIBMAP ioctl; "
108                                 "must have root privileges\n");
109                 }
110         }
111         *phy_blk = b;
112
113         return ret;
114 }
115
116 static void print_extent_header(void)
117 {
118         printf(" ext: %*s %*s length: %*s flags:\n",
119                logical_width * 2 + 3,
120                "logical_offset:",
121                physical_width * 2 + 3, "physical_offset:",
122                physical_width + 1,
123                "expected:");
124 }
125
126 static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex,
127                               unsigned long long expected, int blk_shift,
128                               ext2fs_struct_stat *st)
129 {
130         unsigned long long physical_blk;
131         unsigned long long logical_blk;
132         unsigned long long ext_len;
133         unsigned long long ext_blks;
134         char flags[256] = "";
135
136         /* For inline data all offsets should be in bytes, not blocks */
137         if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE)
138                 blk_shift = 0;
139
140         ext_len = fm_extent->fe_length >> blk_shift;
141         ext_blks = (fm_extent->fe_length - 1) >> blk_shift;
142         logical_blk = fm_extent->fe_logical >> blk_shift;
143         physical_blk = fm_extent->fe_physical >> blk_shift;
144
145         if (expected)
146                 sprintf(flags, ext_fmt == hex_fmt ? "%*llx: " : "%*llu: ",
147                         physical_width, expected >> blk_shift);
148         else
149                 sprintf(flags, "%.*s  ", physical_width, "                   ");
150
151         if (fm_extent->fe_flags & FIEMAP_EXTENT_UNKNOWN)
152                 strcat(flags, "unknown,");
153         if (fm_extent->fe_flags & FIEMAP_EXTENT_DELALLOC)
154                 strcat(flags, "delalloc,");
155         if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_ENCRYPTED)
156                 strcat(flags, "encrypted,");
157         if (fm_extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED)
158                 strcat(flags, "not_aligned,");
159         if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE)
160                 strcat(flags, "inline,");
161         if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_TAIL)
162                 strcat(flags, "tail_packed,");
163         if (fm_extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN)
164                 strcat(flags, "unwritten,");
165         if (fm_extent->fe_flags & FIEMAP_EXTENT_MERGED)
166                 strcat(flags, "merged,");
167
168         if (fm_extent->fe_logical + fm_extent->fe_length >= (__u64) st->st_size)
169                 strcat(flags, "eof,");
170
171         /* Remove trailing comma, if any */
172         if (flags[0])
173                 flags[strlen(flags) - 1] = '\0';
174
175         printf(ext_fmt, cur_ex, logical_width, logical_blk,
176                logical_width, logical_blk + ext_blks,
177                physical_width, physical_blk,
178                physical_width, physical_blk + ext_blks,
179                ext_len, flags);
180 }
181
182 static int filefrag_fiemap(int fd, int blk_shift, int *num_extents,
183                            ext2fs_struct_stat *st)
184 {
185         char buf[16384];
186         struct fiemap *fiemap = (struct fiemap *)buf;
187         struct fiemap_extent *fm_ext = &fiemap->fm_extents[0];
188         int count = (sizeof(buf) - sizeof(*fiemap)) /
189                         sizeof(struct fiemap_extent);
190         unsigned long long expected = 0;
191         unsigned long flags = 0;
192         unsigned int i;
193         static int fiemap_incompat_printed;
194         int fiemap_header_printed = 0;
195         int tot_extents = 0, n = 0;
196         int last = 0;
197         int rc;
198
199         memset(fiemap, 0, sizeof(struct fiemap));
200
201         if (sync_file)
202                 flags |= FIEMAP_FLAG_SYNC;
203
204         if (xattr_map)
205                 flags |= FIEMAP_FLAG_XATTR;
206
207         do {
208                 fiemap->fm_length = ~0ULL;
209                 fiemap->fm_flags = flags;
210                 fiemap->fm_extent_count = count;
211                 rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap);
212                 if (rc < 0) {
213                         if (errno == EBADR && fiemap_incompat_printed == 0) {
214                                 printf("FIEMAP failed with unsupported "
215                                        "flags %x\n", fiemap->fm_flags);
216                                 fiemap_incompat_printed = 1;
217                         }
218                         return rc;
219                 }
220
221                 /* If 0 extents are returned, then more ioctls are not needed */
222                 if (fiemap->fm_mapped_extents == 0)
223                         break;
224
225                 if (verbose && !fiemap_header_printed) {
226                         print_extent_header();
227                         fiemap_header_printed = 1;
228                 }
229
230                 for (i = 0; i < fiemap->fm_mapped_extents; i++) {
231                         if (fm_ext[i].fe_logical != 0 &&
232                             fm_ext[i].fe_physical != expected) {
233                                 tot_extents++;
234                         } else {
235                                 expected = 0;
236                                 if (!tot_extents)
237                                         tot_extents = 1;
238                         }
239                         if (verbose)
240                                 print_extent_info(&fm_ext[i], n, expected,
241                                                   blk_shift, st);
242
243                         expected = fm_ext[i].fe_physical + fm_ext[i].fe_length;
244                         if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST)
245                                 last = 1;
246                         n++;
247                 }
248
249                 fiemap->fm_start = (fm_ext[i - 1].fe_logical +
250                                     fm_ext[i - 1].fe_length);
251         } while (last == 0);
252
253         *num_extents = tot_extents;
254
255         return 0;
256 }
257
258 #define EXT2_DIRECT     12
259
260 static int filefrag_fibmap(int fd, int blk_shift, int *num_extents,
261                            ext2fs_struct_stat *st,
262                            unsigned long numblocks, int is_ext2)
263 {
264         struct fiemap_extent    fm_ext;
265         unsigned long           i, last_block;
266         unsigned long long      logical;
267                                 /* Blocks per indirect block */
268         const long              bpib = st->st_blksize / 4;
269         int                     count;
270
271         if (force_extent) {
272                 memset(&fm_ext, 0, sizeof(fm_ext));
273                 fm_ext.fe_flags = FIEMAP_EXTENT_MERGED;
274         }
275
276         if (sync_file)
277                 fsync(fd);
278
279         for (i = 0, logical = 0, *num_extents = 0, count = last_block = 0;
280              i < numblocks;
281              i++, logical += st->st_blksize) {
282                 unsigned long block = 0;
283                 int rc;
284
285                 if (is_ext2 && last_block) {
286                         if (((i - EXT2_DIRECT) % bpib) == 0)
287                                 last_block++;
288                         if (((i - EXT2_DIRECT - bpib) % (bpib * bpib)) == 0)
289                                 last_block++;
290                         if (((i - EXT2_DIRECT - bpib - bpib * bpib) %
291                              (((unsigned long long)bpib) * bpib * bpib)) == 0)
292                                 last_block++;
293                 }
294                 rc = get_bmap(fd, i, &block);
295                 if (rc < 0)
296                         return rc;
297                 if (block == 0)
298                         continue;
299                 if (*num_extents == 0) {
300                         (*num_extents)++;
301                         if (force_extent) {
302                                 print_extent_header();
303                                 fm_ext.fe_physical = block * st->st_blksize;
304                         }
305                 }
306                 count++;
307                 if (force_extent && last_block != 0 &&
308                     (block != last_block + 1 ||
309                      fm_ext.fe_logical + fm_ext.fe_length != logical)) {
310                         print_extent_info(&fm_ext, *num_extents - 1,
311                                           (last_block + 1) * st->st_blksize,
312                                           blk_shift, st);
313                         fm_ext.fe_logical = logical;
314                         fm_ext.fe_physical = block * st->st_blksize;
315                         fm_ext.fe_length = 0;
316                         (*num_extents)++;
317                 } else if (verbose && last_block && (block != last_block + 1)) {
318                         printf("Discontinuity: Block %ld is at %lu (was %lu)\n",
319                                i, block, last_block + 1);
320                         (*num_extents)++;
321                 }
322                 fm_ext.fe_length += st->st_blksize;
323                 last_block = block;
324         }
325
326         if (force_extent)
327                 print_extent_info(&fm_ext, *num_extents - 1,
328                                   last_block * st->st_blksize, blk_shift, st);
329
330         return count;
331 }
332
333 static void frag_report(const char *filename)
334 {
335         static struct statfs fsinfo;
336         ext2fs_struct_stat st;
337         int             blk_shift;
338         long            fd;
339         unsigned long   numblocks;
340         int             data_blocks_per_cyl = 1;
341         int             num_extents = 1, expected = ~0;
342         int             is_ext2 = 0;
343         static dev_t    last_device;
344         unsigned int    flags;
345         int             width;
346
347 #if defined(HAVE_OPEN64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
348         fd = open64(filename, O_RDONLY);
349 #else
350         fd = open(filename, O_RDONLY);
351 #endif
352         if (fd < 0) {
353                 perror("open");
354                 return;
355         }
356
357 #if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED)
358         if (fstat64(fd, &st) < 0) {
359 #else
360         if (fstat(fd, &st) < 0) {
361 #endif
362                 close(fd);
363                 perror("stat");
364                 return;
365         }
366
367         if (last_device != st.st_dev) {
368                 if (fstatfs(fd, &fsinfo) < 0) {
369                         close(fd);
370                         perror("fstatfs");
371                         return;
372                 }
373                 if (verbose)
374                         printf("Filesystem type is: %lx\n",
375                                (unsigned long) fsinfo.f_type);
376         }
377         st.st_blksize = fsinfo.f_bsize;
378         if (ioctl(fd, EXT3_IOC_GETFLAGS, &flags) < 0)
379                 flags = 0;
380         if (!(flags & EXT4_EXTENTS_FL) &&
381             ((fsinfo.f_type == 0xef51) || (fsinfo.f_type == 0xef52) ||
382              (fsinfo.f_type == 0xef53)))
383                 is_ext2++;
384
385         if (is_ext2) {
386                 long cylgroups = div_ceil(fsinfo.f_blocks, fsinfo.f_bsize * 8);
387
388                 if (verbose && last_device != st.st_dev)
389                         printf("Filesystem cylinder groups approximately %ld\n",
390                                cylgroups);
391
392                 data_blocks_per_cyl = fsinfo.f_bsize * 8 -
393                                         (fsinfo.f_files / 8 / cylgroups) - 3;
394         }
395         last_device = st.st_dev;
396
397         width = int_log10(fsinfo.f_blocks);
398         if (width > physical_width)
399                 physical_width = width;
400
401         numblocks = (st.st_size + fsinfo.f_bsize - 1) / fsinfo.f_bsize;
402         if (blocksize != 0)
403                 blk_shift = int_log2(blocksize);
404         else
405                 blk_shift = int_log2(fsinfo.f_bsize);
406
407         width = int_log10(numblocks);
408         if (width > logical_width)
409                 logical_width = width;
410         if (verbose)
411                 printf("File size of %s is %llu (%lu block%s of %d bytes)\n",
412                        filename, (unsigned long long)st.st_size,
413                        numblocks * fsinfo.f_bsize >> blk_shift,
414                        numblocks == 1 ? "" : "s", 1 << blk_shift);
415
416         if (force_bmap ||
417             filefrag_fiemap(fd, blk_shift, &num_extents, &st) != 0) {
418                 expected = filefrag_fibmap(fd, blk_shift, &num_extents,
419                                            &st, numblocks, is_ext2);
420                 if (expected < 0) {
421                         if (errno == EINVAL || errno == ENOTTY) {
422                                 fprintf(stderr, "%s: FIBMAP unsupported\n",
423                                         filename);
424                         } else if (errno != EPERM) {
425                                 fprintf(stderr, "%s: FIBMAP error: %s",
426                                         filename, strerror(errno));
427                         }
428                         goto out_close;
429                 }
430                 expected = expected / data_blocks_per_cyl + 1;
431         }
432
433         if (num_extents == 1)
434                 printf("%s: 1 extent found", filename);
435         else
436                 printf("%s: %d extents found", filename, num_extents);
437         /* count, and thus expected, only set for indirect FIBMAP'd files */
438         if (is_ext2 && expected && expected < num_extents)
439                 printf(", perfection would be %d extent%s\n", expected,
440                         (expected > 1) ? "s" : "");
441         else
442                 fputc('\n', stdout);
443 out_close:
444         close(fd);
445 }
446
447 static void usage(const char *progname)
448 {
449         fprintf(stderr, "Usage: %s [-b{blocksize}] [-BeklsvxX] file ...\n",
450                 progname);
451         exit(1);
452 }
453
454 int main(int argc, char**argv)
455 {
456         char **cpp;
457         int c;
458
459         while ((c = getopt(argc, argv, "Bb::eksvxX")) != EOF)
460                 switch (c) {
461                 case 'B':
462                         force_bmap++;
463                         break;
464                 case 'b':
465                         if (optarg) {
466                                 char *end;
467                                 blocksize = strtoul(optarg, &end, 0);
468                                 if (end) {
469                                         switch (end[0]) {
470                                         case 'g':
471                                         case 'G':
472                                                 blocksize *= 1024;
473                                                 /* no break */
474                                         case 'm':
475                                         case 'M':
476                                                 blocksize *= 1024;
477                                                 /* no break */
478                                         case 'k':
479                                         case 'K':
480                                                 blocksize *= 1024;
481                                                 break;
482                                         default:
483                                                 break;
484                                         }
485                                 }
486                         } else { /* Allow -b without argument for compat. Remove
487                                   * this eventually so "-b {blocksize}" works */
488                                 fprintf(stderr, "%s: -b needs a blocksize "
489                                         "option, assuming 1024-byte blocks.\n",
490                                         argv[0]);
491                                 blocksize = 1024;
492                         }
493                         break;
494                 case 'e':
495                         force_extent++;
496                         if (!verbose)
497                                 verbose++;
498                         break;
499                 case 'k':
500                         blocksize = 1024;
501                         break;
502                 case 's':
503                         sync_file++;
504                         break;
505                 case 'v':
506                         verbose++;
507                         break;
508                 case 'x':
509                         xattr_map++;
510                         break;
511                 case 'X':
512                         ext_fmt = hex_fmt;
513                         break;
514                 default:
515                         usage(argv[0]);
516                         break;
517                 }
518         if (optind == argc)
519                 usage(argv[0]);
520         for (cpp=argv+optind; *cpp; cpp++)
521                 frag_report(*cpp);
522         return 0;
523 }
524 #endif