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