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