Whamcloud - gitweb
Initial checkin of the filefrag program, which reports on how
authorTheodore Ts'o <tytso@mit.edu>
Wed, 17 Dec 2003 15:13:41 +0000 (10:13 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Wed, 17 Dec 2003 15:13:41 +0000 (10:13 -0500)
badly fragmented a file might be.

misc/Makefile.in
misc/filefrag.8.in [new file with mode: 0644]
misc/filefrag.c [new file with mode: 0644]

index 2a3c644..a400bc1 100644 (file)
@@ -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 (file)
index 0000000..1077c9d
--- /dev/null
@@ -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 <tytso@mit.edu>.
diff --git a/misc/filefrag.c b/misc/filefrag.c
new file mode 100644 (file)
index 0000000..043e0ba
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <linux/fd.h>
+
+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;
+}
+
+
+