filefrag.c revision 3d16b3f4bbb4c1f43a442e303658a7a50320c40a
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/*
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * filefrag.c -- report if a particular file is fragmented
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copyright 2003 by Theodore Ts'o.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * %Begin-Header%
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * This file may be redistributed under the terms of the GNU Public
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * License.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * %End-Header%
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef __linux__
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include <stdlib.h>
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int main(void) {
17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    fputs("This program is only supported on Linux!\n", stderr);
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit(EXIT_FAILURE);
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define _LARGEFILE64_SOURCE
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdlib.h>
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <unistd.h>
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string.h>
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <time.h>
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <fcntl.h>
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <errno.h>
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifdef HAVE_GETOPT_H
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <getopt.h>
327dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#else
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)extern char *optarg;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)extern int optind;
351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#endif
361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include <sys/types.h>
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/stat.h>
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sys/vfs.h>
391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include <sys/ioctl.h>
401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include <linux/fd.h>
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int verbose = 0;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#define FIBMAP	   _IO(0x00,1)	/* bmap access */
45e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#define FIGETBSZ   _IO(0x00,2)	/* get the block size used for bmap */
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define EXT3_EXTENTS_FL			0x00080000 /* Inode uses extents */
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define	EXT3_IOC_GETFLAGS		_IOR('f', 1, long)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static unsigned long get_bmap(int fd, unsigned long block)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int	ret;
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)	unsigned long b;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	b = block;
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)	ret = ioctl(fd, FIBMAP, &b);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (ret < 0) {
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (errno == EPERM) {
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			fprintf(stderr, "No permission to use FIBMAP ioctl; must have root privileges\n");
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			exit(1);
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		perror("FIBMAP");
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return b;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define EXT2_DIRECT	12
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void frag_report(const char *filename)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct statfs	fsinfo;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	struct stat64	fileinfo;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	long		i, fd, bs, block, last_block = 0, numblocks;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	long		bpib;	/* Blocks per indirect block */
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	long		cylgroups;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int		discont = 0, expected;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int		is_ext2 = 0;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	unsigned int	flags;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (statfs(filename, &fsinfo) < 0) {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		perror("statfs");
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (stat64(filename, &fileinfo) < 0) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		perror("stat");
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (!S_ISREG(fileinfo.st_mode)) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("%s: Not a regular file\n", filename);
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if ((fsinfo.f_type == 0xef51) || (fsinfo.f_type == 0xef52) ||
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	    (fsinfo.f_type == 0xef53))
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		is_ext2++;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (verbose) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("Filesystem type is: %x\n", fsinfo.f_type);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	cylgroups = (fsinfo.f_blocks + fsinfo.f_bsize*8-1) / fsinfo.f_bsize*8;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (verbose) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("Filesystem cylinder groups is approximately %ld\n",
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		       cylgroups);
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	fd = open(filename, O_RDONLY | O_LARGEFILE);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (fd < 0) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		perror("open");
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (ioctl(fd, FIGETBSZ, &bs) < 0) {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		perror("FIGETBSZ");
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		close(fd);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (ioctl(fd, EXT3_IOC_GETFLAGS, &flags) < 0) {
114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		perror("EXT3_IOC_GETFLAGS");
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		close(fd);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		return;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (flags & EXT3_EXTENTS_FL) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("File is stored in extents format\n");
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		is_ext2 = 0;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (verbose)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("Blocksize of file %s is %ld\n", filename, bs);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	bpib = bs / 4;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	numblocks = (fileinfo.st_size + (bs-1)) / bs;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (verbose) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("File size of %s is %lld (%ld blocks)\n", filename,
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		       (long long) fileinfo.st_size, numblocks);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf("First block: %ld\nLast block: %ld\n",
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		       get_bmap(fd, 0), get_bmap(fd, numblocks - 1));
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (i=0; i < numblocks; i++) {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (is_ext2) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (((i-EXT2_DIRECT) % bpib) == 0)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				last_block++;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (((i-EXT2_DIRECT-bpib) % (bpib*bpib)) == 0)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				last_block++;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (((i-EXT2_DIRECT-bpib-bpib*bpib) % (bpib*bpib*bpib)) == 0)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				last_block++;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		block = get_bmap(fd, i);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (block == 0)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			continue;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (last_block && (block != last_block +1) ) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			if (verbose)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				printf("Discontinuity: Block %ld is at %ld (was %ld)\n",
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)				       i, block, last_block);
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			discont++;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		last_block = block;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (discont==0)
153e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch		printf("%s: 1 extent found", filename);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	else
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)		printf("%s: %d extents found", filename, discont+1);
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)	expected = (numblocks/((bs*8)-(fsinfo.f_files/8/cylgroups)-3))+1;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (is_ext2 && expected != discont+1)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		printf(", perfection would be %d extent%s\n", expected,
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			(expected>1) ? "s" : "");
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	else
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		fputc('\n', stdout);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	close(fd);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void usage(const char *progname)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	fprintf(stderr, "Usage: %s [-v] file ...\n", progname);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	exit(1);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int main(int argc, char**argv)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	char **cpp;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	int c;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	while ((c = getopt(argc, argv, "v")) != EOF)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		switch (c) {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		case 'v':
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			verbose++;
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			break;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		default:
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			usage(argv[0]);
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			break;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		}
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	if (optind == argc)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		usage(argv[0]);
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	for (cpp=argv+optind; *cpp; cpp++) {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		if (verbose)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)			printf("Checking %s\n", *cpp);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		frag_report(*cpp);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	}
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	return 0;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)