filefrag.c revision f97ea10fde6d8bee670d388f02cf9524b8f2d7da
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 17int 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 34extern char *optarg; 35extern 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/ext2_types.h> 43#include <ext2fs/fiemap.h> 44 45int verbose = 0; 46int no_bs = 0; /* Don't use the files blocksize, use 1K blocksize */ 47int sync_file = 0; /* fsync file before getting the mapping */ 48int xattr_map = 0; /* get xattr mapping */ 49int logical_width = 12; 50int physical_width = 14; 51unsigned long long filesize; 52 53#define FILEFRAG_FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) 54 55#define FIBMAP _IO(0x00, 1) /* bmap access */ 56#define FIGETBSZ _IO(0x00, 2) /* get the block size used for bmap */ 57#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap) 58 59#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ 60#define EXT3_IOC_GETFLAGS _IOR('f', 1, long) 61 62static int int_log2(int arg) 63{ 64 int l = 0; 65 66 arg >>= 1; 67 while (arg) { 68 l++; 69 arg >>= 1; 70 } 71 return l; 72} 73 74static int int_log10(unsigned long long arg) 75{ 76 int l = 0; 77 78 arg = arg / 10; 79 while (arg) { 80 l++; 81 arg = arg / 10; 82 } 83 return l; 84} 85 86static unsigned int div_ceil(unsigned int a, unsigned int b) 87{ 88 if (!a) 89 return 0; 90 return ((a - 1) / b) + 1; 91} 92 93static int get_bmap(int fd, unsigned long block, unsigned long *phy_blk) 94{ 95 int ret; 96 unsigned int b; 97 98 printf("Calling get_bmap for block %lu\n", block); 99 abort(); 100 b = block; 101 ret = ioctl(fd, FIBMAP, &b); /* FIBMAP takes pointer to integer */ 102 if (ret < 0) { 103 if (errno == EPERM) { 104 fprintf(stderr, "No permission to use FIBMAP ioctl; " 105 "must have root privileges\n"); 106 exit(1); 107 } 108 perror("FIBMAP"); 109 } 110 *phy_blk = b; 111 112 return ret; 113} 114 115static void print_extent_info(struct fiemap_extent *fm_extent, int cur_ex, 116 unsigned long long expected, int blk_shift) 117{ 118 __u64 phy_blk; 119 unsigned long long logical_blk; 120 unsigned long ext_len; 121 char flags[256] = ""; 122 123 /* For inline data all offsets should be in terms of bytes, not blocks */ 124 if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE) 125 blk_shift = 0; 126 127 ext_len = fm_extent->fe_length >> blk_shift; 128 logical_blk = fm_extent->fe_logical >> blk_shift; 129 phy_blk = fm_extent->fe_physical >> blk_shift; 130 131 if (fm_extent->fe_flags & FIEMAP_EXTENT_UNKNOWN) 132 strcat(flags, "unknown,"); 133 if (fm_extent->fe_flags & FIEMAP_EXTENT_DELALLOC) 134 strcat(flags, "delalloc,"); 135 if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_ENCRYPTED) 136 strcat(flags, "encrypted,"); 137 if (fm_extent->fe_flags & FIEMAP_EXTENT_NOT_ALIGNED) 138 strcat(flags, "not_aligned,"); 139 if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_INLINE) 140 strcat(flags, "inline,"); 141 if (fm_extent->fe_flags & FIEMAP_EXTENT_DATA_TAIL) 142 strcat(flags, "tail_packed,"); 143 if (fm_extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN) 144 strcat(flags, "unwritten,"); 145 if (fm_extent->fe_flags & FIEMAP_EXTENT_MERGED) 146 strcat(flags, "merged,"); 147 148 if (fm_extent->fe_logical + fm_extent->fe_length >= filesize) 149 strcat(flags, "eof,"); 150 151 /* Remove trailing comma, if any */ 152 if (flags[0]) 153 flags[strlen(flags) - 1] = '\0'; 154 155 if (expected) 156 printf("%4d %*llu %*llu %*llu %6lu %s\n", 157 cur_ex, logical_width, logical_blk, 158 physical_width, phy_blk, physical_width, expected, 159 ext_len, flags); 160 else 161 printf("%4d %*llu %*llu %*s %6lu %s\n", 162 cur_ex, logical_width, logical_blk, 163 physical_width, phy_blk, physical_width, "", 164 ext_len, flags); 165} 166 167int filefrag_fiemap(int fd, int blk_shift, int *num_extents) 168{ 169 char buf[4096] = ""; 170 struct fiemap *fiemap = (struct fiemap *)buf; 171 struct fiemap_extent *fm_ext = &fiemap->fm_extents[0]; 172 int count = (sizeof(buf) - sizeof(*fiemap)) / 173 sizeof(struct fiemap_extent); 174 unsigned long long logical_blk = 0, last_blk = 0; 175 unsigned long flags = 0; 176 static int fiemap_incompat_printed; 177 int tot_extents = 1, n = 0; 178 int last = 0, eof = 0; 179 int i, rc; 180 181 fiemap->fm_length = ~0ULL; 182 183 memset(fiemap, 0, sizeof(struct fiemap)); 184 185 if (!verbose) 186 count = 0; 187 188 if (sync_file) 189 flags |= FIEMAP_FLAG_SYNC; 190 191 if (xattr_map) 192 flags |= FIEMAP_FLAG_XATTR; 193 194 if (verbose) 195 printf(" ext %*s %*s %*s length flags\n", logical_width, 196 "logical", physical_width, "physical", 197 physical_width, "expected"); 198 199 do { 200 fiemap->fm_length = ~0ULL; 201 fiemap->fm_flags = flags; 202 fiemap->fm_extent_count = count; 203 rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap); 204 if (rc < 0) { 205 if (errno == EBADR && fiemap_incompat_printed == 0) { 206 printf("FIEMAP failed with unsupported " 207 "flags %x\n", fiemap->fm_flags); 208 fiemap_incompat_printed = 1; 209 } 210 return rc; 211 } 212 213 if (!verbose) { 214 *num_extents = fiemap->fm_mapped_extents; 215 goto out; 216 } 217 218 /* If 0 extents are returned, then more ioctls are not needed */ 219 if (fiemap->fm_mapped_extents == 0) 220 break; 221 222 for (i = 0; i < fiemap->fm_mapped_extents; i++) { 223 __u64 phy_blk, phy_start, logical_blk; 224 unsigned long ext_len; 225 226 phy_blk = fm_ext[i].fe_physical >> blk_shift; 227 ext_len = fm_ext[i].fe_length >> blk_shift; 228 logical_blk = fm_ext[i].fe_logical >> blk_shift; 229 230 if (logical_blk && phy_blk != last_blk + 1) 231 tot_extents++; 232 else 233 last_blk = 0; 234 print_extent_info(&fm_ext[i], n, last_blk, blk_shift); 235 236 last_blk = phy_blk + ext_len - 1; 237 if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST) 238 last = 1; 239 n++; 240 } 241 242 fiemap->fm_start = (fm_ext[i-1].fe_logical + 243 fm_ext[i-1].fe_length); 244 } while (last == 0); 245 246 *num_extents = tot_extents; 247out: 248 return 0; 249} 250 251#define EXT2_DIRECT 12 252 253static void frag_report(const char *filename) 254{ 255 struct statfs fsinfo; 256#ifdef HAVE_FSTAT64 257 struct stat64 fileinfo; 258#else 259 struct stat fileinfo; 260#endif 261 int bs; 262 long fd; 263 unsigned long block, last_block = 0, numblocks, i, count; 264 long bpib; /* Blocks per indirect block */ 265 long cylgroups; 266 int num_extents = 0, expected; 267 int is_ext2 = 0; 268 static int once = 1; 269 unsigned int flags; 270 unsigned long first_blk, last_blk; 271 int rc; 272 273#ifdef HAVE_OPEN64 274 fd = open64(filename, O_RDONLY); 275#else 276 fd = open(filename, O_RDONLY); 277#endif 278 if (fd < 0) { 279 perror("open"); 280 return; 281 } 282 283 if (statfs(filename, &fsinfo) < 0) { 284 perror("statfs"); 285 return; 286 } 287#ifdef HAVE_FSTAT64 288 if (stat64(filename, &fileinfo) < 0) { 289#else 290 if (stat(filename, &fileinfo) < 0) { 291#endif 292 perror("stat"); 293 return; 294 } 295 if (ioctl(fd, EXT3_IOC_GETFLAGS, &flags) < 0) 296 flags = 0; 297 if (!(flags & EXT4_EXTENTS_FL) && 298 ((fsinfo.f_type == 0xef51) || (fsinfo.f_type == 0xef52) || 299 (fsinfo.f_type == 0xef53))) 300 is_ext2++; 301 if (verbose && once) 302 printf("Filesystem type is: %lx\n", 303 (unsigned long) fsinfo.f_type); 304 305 cylgroups = div_ceil(fsinfo.f_blocks, fsinfo.f_bsize*8); 306 if (verbose && is_ext2 && once) 307 printf("Filesystem cylinder groups is approximately %ld\n", 308 cylgroups); 309 310 physical_width = int_log10(fsinfo.f_blocks); 311 if (physical_width < 8) 312 physical_width = 8; 313 314 if (ioctl(fd, FIGETBSZ, &bs) < 0) { /* FIGETBSZ takes an int */ 315 perror("FIGETBSZ"); 316 close(fd); 317 return; 318 } 319 320 if (no_bs) 321 bs = 1024; 322 323 bpib = bs / 4; 324 numblocks = (fileinfo.st_size + (bs-1)) / bs; 325 logical_width = int_log10(numblocks); 326 if (logical_width < 7) 327 logical_width = 7; 328 filesize = (long long)fileinfo.st_size; 329 if (verbose) 330 printf("File size of %s is %lld (%ld block%s, blocksize %d)\n", 331 filename, (long long) fileinfo.st_size, numblocks, 332 numblocks == 1 ? "" : "s", bs); 333 if (filefrag_fiemap(fd, int_log2(bs), &num_extents) != 0) { 334 for (i = 0; i < numblocks; i++) { 335 if (is_ext2 && last_block) { 336 if (((i-EXT2_DIRECT) % bpib) == 0) 337 last_block++; 338 if (((i-EXT2_DIRECT-bpib) % (bpib*bpib)) == 0) 339 last_block++; 340 if (((i-EXT2_DIRECT-bpib-bpib*bpib) % 341 (bpib*bpib*bpib)) == 0) 342 last_block++; 343 } 344 rc = get_bmap(fd, i, &block); 345 if (block == 0) 346 continue; 347 if (last_block && (block != last_block+1) ) { 348 if (verbose) 349 printf("Discontinuity: Block %ld is at " 350 "%lu (was %lu)\n", 351 i, block, last_block+1); 352 num_extents++; 353 } 354 last_block = block; 355 } 356 } 357 if (num_extents == 1) 358 printf("%s: 1 extent found", filename); 359 else 360 printf("%s: %d extents found", filename, num_extents); 361 expected = (count/((bs*8)-(fsinfo.f_files/8/cylgroups)-3))+1; 362 if (is_ext2 && expected < num_extents) 363 printf(", perfection would be %d extent%s\n", expected, 364 (expected>1) ? "s" : ""); 365 else 366 fputc('\n', stdout); 367 close(fd); 368 once = 0; 369} 370 371static void usage(const char *progname) 372{ 373 fprintf(stderr, "Usage: %s [-bvsx] file ...\n", progname); 374 exit(1); 375} 376 377int main(int argc, char**argv) 378{ 379 char **cpp; 380 int c; 381 int ret; 382 383 while ((c = getopt(argc, argv, "bsvx")) != EOF) 384 switch (c) { 385 case 'b': 386 no_bs++; 387 break; 388 case 'v': 389 verbose++; 390 break; 391 case 's': 392 sync_file++; 393 break; 394 case 'x': 395 xattr_map++; 396 break; 397 default: 398 usage(argv[0]); 399 break; 400 } 401 if (optind == argc) 402 usage(argv[0]); 403 for (cpp=argv+optind; *cpp; cpp++) 404 frag_report(*cpp); 405 return 0; 406} 407#endif 408