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