From 9642413014c0c037339c392cdc20343c4e80f079 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Wed, 17 Dec 2003 10:13:41 -0500 Subject: [PATCH] Initial checkin of the filefrag program, which reports on how badly fragmented a file might be. --- misc/Makefile.in | 17 +++++- misc/filefrag.8.in | 24 ++++++++ misc/filefrag.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 misc/filefrag.8.in create mode 100644 misc/filefrag.c diff --git a/misc/Makefile.in b/misc/Makefile.in index 2a3c644..a400bc1 100644 --- a/misc/Makefile.in +++ b/misc/Makefile.in @@ -35,11 +35,13 @@ BADBLOCKS_OBJS= badblocks.o E2IMAGE_OBJS= e2image.o FSCK_OBJS= fsck.o base_device.o BLKID_OBJS= blkid.o +FILEFRAG_OBJS= filefrag.o SRCS= $(srcdir)/tune2fs.c $(srcdir)/mklost+found.c $(srcdir)/mke2fs.c \ $(srcdir)/chattr.c $(srcdir)/lsattr.c $(srcdir)/dumpe2fs.c \ $(srcdir)/badblocks.c $(srcdir)/fsck.c $(srcdir)/util.c \ - $(srcdir)/uuidgen.c $(srcdir)/blkid.c + $(srcdir)/uuidgen.c $(srcdir)/blkid.c $(srcdir)/logsave.c \ + $(srcdir)/filefrag.c $(srcdir)/base_device.c LIBS= $(LIBEXT2FS) $(LIBCOM_ERR) DEPLIBS= $(LIBEXT2FS) $(LIBCOM_ERR) @@ -56,7 +58,7 @@ DEPLIBS_E2P= $(LIBE2P) $(LIBCOM_ERR) .c.o: $(CC) -c $(ALL_CFLAGS) $< -o $@ -all:: $(SPROGS) $(UPROGS) $(USPROGS) $(SMANPAGES) $(UMANPAGES) +all:: $(SPROGS) $(UPROGS) $(USPROGS) $(SMANPAGES) $(UMANPAGES) filefrag findsuper: findsuper.o $(CC) $(ALL_LDFLAGS) -o findsuper findsuper.o @@ -115,6 +117,9 @@ badblocks: $(BADBLOCKS_OBJS) $(DEPLIBS) logsave: logsave.o $(CC) $(ALL_LDFLAGS) -o logsave logsave.o +filefrag: $(FILEFRAG_OBJS) + $(CC) $(ALL_LDFLAGS) -o filefrag $(FILEFRAG_OBJS) + tune2fs.8: $(DEP_SUBSTITUTE) $(srcdir)/tune2fs.8.in $(SUBSTITUTE) $(srcdir)/tune2fs.8.in tune2fs.8 @@ -160,6 +165,9 @@ uuidgen.1: $(DEP_SUBSTITUTE) $(srcdir)/uuidgen.1.in blkid.1: $(DEP_SUBSTITUTE) $(srcdir)/blkid.1.in $(SUBSTITUTE) $(srcdir)/blkid.1.in blkid.1 +filefrag.8: $(DEP_SUBSTITUTE) $(srcdir)/filefrag.8.in + $(SUBSTITUTE) $(srcdir)/filefrag.8.in filefrag.8 + installdirs: $(top_srcdir)/mkinstalldirs $(DESTDIR)$(sbindir) \ $(DESTDIR)$(root_sbindir) $(DESTDIR)$(bindir) \ @@ -224,7 +232,7 @@ uninstall: clean: $(RM) -f $(SPROGS) $(USPROGS) $(UPROGS) $(UMANPAGES) $(SMANPAGES) \ - base_device base_device.out mke2fs.static \ + base_device base_device.out mke2fs.static filefrag \ \#* *.s *.o *.a *~ core mostlyclean: clean @@ -288,3 +296,6 @@ uuidgen.o: $(srcdir)/uuidgen.c $(top_srcdir)/lib/uuid/uuid.h \ $(srcdir)/nls-enable.h blkid.o: $(srcdir)/blkid.c $(top_srcdir)/lib/blkid/blkid.h \ $(top_builddir)/lib/blkid/blkid_types.h +logsave.o: $(srcdir)/logsave.c +filefrag.o: $(srcdir)/filefrag.c +base_device.o: $(srcdir)/base_device.c $(srcdir)/fsck.h diff --git a/misc/filefrag.8.in b/misc/filefrag.8.in new file mode 100644 index 0000000..1077c9d --- /dev/null +++ b/misc/filefrag.8.in @@ -0,0 +1,24 @@ +.\" -*- nroff -*- +.TH FILEFRAG 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@" +.SH NAME +filefrag \- report on file fragmentation +.SH SYNOPSIS +.B filefrag +[ +.B \-v +] +[ +.I files... +] +.SH DESCRIPTION +.B filefrag +reports on how badly fragmented a particular file might be. It makes +allowances for indirect blocks for ext2 and ext3 filesystems, but can be +used on files for any filesystem. +.SH OPTIONS +.TP +.B \-v +Be verbose when checking for file fragmentation. +.SH AUTHOR +.B filefrag +was written by Theodore Ts'o . diff --git a/misc/filefrag.c b/misc/filefrag.c new file mode 100644 index 0000000..043e0ba --- /dev/null +++ b/misc/filefrag.c @@ -0,0 +1,162 @@ +/* + * filefrag.c -- report if a particular file is fragmented + * + * Copyright 2003 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#define _LARGEFILE64_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int verbose = 0; + +#define FIBMAP _IO(0x00,1) /* bmap access */ +#define FIGETBSZ _IO(0x00,2) /* get the block size used for bmap */ + +static unsigned long get_bmap(int fd, unsigned long block) +{ + int ret; + unsigned long b; + + b = block; + ret = ioctl(fd, FIBMAP, &b); + if (ret < 0) { + if (errno == EPERM) { + fprintf(stderr, "No permission to use FIBMAP ioctl; must have root privileges\n"); + exit(1); + } + perror("FIBMAP"); + } + return b; +} + +#define EXT2_DIRECT 12 + +void frag_report(const char *filename) +{ + struct statfs fsinfo; + struct stat64 fileinfo; + long i, fd, bs, block, last_block, numblocks; + long bpib; /* Blocks per indirect block */ + long cylgroups; + int discont = 0, expected; + int is_ext2 = 0; + + if (statfs(filename, &fsinfo) < 0) { + perror("statfs"); + return; + } + if (stat64(filename, &fileinfo) < 0) { + perror("stat"); + return; + } + if (!S_ISREG(fileinfo.st_mode)) { + printf("%s: Not a regular file\n", filename); + return; + } + if ((fsinfo.f_type == 0xef51) || (fsinfo.f_type == 0xef52) || + (fsinfo.f_type == 0xef53)) + is_ext2++; + if (verbose) { + printf("Filesystem type is: %lx\n", fsinfo.f_type); + } + cylgroups = (fsinfo.f_blocks + fsinfo.f_bsize*8-1) / fsinfo.f_bsize*8; + if (verbose) { + printf("Filesystem cylinder groups is approximately %ld\n", + cylgroups); + } + fd = open(filename, O_RDONLY | O_LARGEFILE); + if (fd < 0) { + perror("open"); + return; + } + if (ioctl(fd, FIGETBSZ, &bs) < 0) { + perror("FIGETBSZ"); + return; + } + if (verbose) + printf("Blocksize of file %s is %d\n", filename, bs); + bpib = bs / 4; + numblocks = (fileinfo.st_size + (bs-1)) / bs; + if (verbose) + printf("File size of %s is %lld (%d blocks)\n", filename, + (long long) fileinfo.st_size, numblocks); + for (i=0; i < numblocks; i++) { + if (is_ext2) { + if (((i-EXT2_DIRECT) % bpib) == 0) + last_block++; + if (((i-EXT2_DIRECT-bpib) % (bpib*bpib)) == 0) + last_block++; + if (((i-EXT2_DIRECT-bpib-bpib*bpib) % (bpib*bpib*bpib)) == 0) + last_block++; + } + block = get_bmap(fd, i); + if (i && (block != last_block +1) ) { + if (verbose) + printf("Discontinuity: Block %ld is at %ld (was %ld)\n", + i, block, last_block); + discont++; + } + if (block) + last_block = block; + } + if (discont==0) + printf("%s: 1 extent found", filename); + else + printf("%s: %d extents found", filename, discont+1); + expected = (numblocks/((bs*8)-(fsinfo.f_files/8/cylgroups)-3))+1; + if (is_ext2 && expected != discont+1) + printf(", perfection would be %d extent%s\n", expected, + (expected>1) ? "s" : ""); + else + fputc('\n', stdout); + +} + +void usage(const char *progname) +{ + fprintf(stderr, "Usage: %s [-v] file ...\n", progname); + exit(1); +} + +int main(int argc, char**argv) +{ + char **cpp; + int c; + + while ((c = getopt(argc, argv, "v")) != EOF) + switch (c) { + case 'v': + verbose++; + break; + default: + usage(argv[0]); + break; + } + if (optind == argc) + usage(argv[0]); + for (cpp=argv+optind; *cpp; cpp++) { + if (verbose) + printf("Checking %s\n", *cpp); + frag_report(*cpp); + } + return 0; +} + + + -- 1.8.3.1