Message ID | 1321564364-22803-1-git-send-email-tytso@mit.edu |
---|---|
State | Accepted, archived |
Headers | show |
On 11/17/11 3:12 PM, Theodore Ts'o wrote: > Add the ability to report on the fragmentation of a file on a file > system opened using debugfs. > > Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> > --- > debugfs/Makefile.in | 8 +- > debugfs/debug_cmds.ct | 3 + > debugfs/debugfs.8.in | 21 +++ > debugfs/debugfs.h | 1 + > debugfs/filefrag.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++ Is it possible to share any of ^^^ that code ^^^ with misc/filefrag.c somehow? -Eric > debugfs/ro_debug_cmds.ct | 3 + > 6 files changed, 357 insertions(+), 3 deletions(-) > create mode 100644 debugfs/filefrag.c > > diff --git a/debugfs/Makefile.in b/debugfs/Makefile.in > index e03a3c6..c6aaa3a 100644 > --- a/debugfs/Makefile.in > +++ b/debugfs/Makefile.in > @@ -17,15 +17,17 @@ MANPAGES= debugfs.8 > MK_CMDS= _SS_DIR_OVERRIDE=../lib/ss ../lib/ss/mk_cmds > > DEBUG_OBJS= debug_cmds.o debugfs.o util.o ncheck.o icheck.o ls.o \ > - lsdel.o dump.o set_fields.o logdump.o htree.o unused.o e2freefrag.o > + lsdel.o dump.o set_fields.o logdump.o htree.o unused.o e2freefrag.o \ > + filefrag.o > > RO_DEBUG_OBJS= ro_debug_cmds.o ro_debugfs.o util.o ncheck.o icheck.o ls.o \ > - lsdel.o logdump.o htree.o e2freefrag.o > + lsdel.o logdump.o htree.o e2freefrag.o filefrag.o > > SRCS= debug_cmds.c $(srcdir)/debugfs.c $(srcdir)/util.c $(srcdir)/ls.c \ > $(srcdir)/ncheck.c $(srcdir)/icheck.c $(srcdir)/lsdel.c \ > $(srcdir)/dump.c $(srcdir)/set_fields.c ${srcdir}/logdump.c \ > - $(srcdir)/htree.c $(srcdir)/unused.c > + $(srcdir)/htree.c $(srcdir)/unused.c ${srcdir}/../misc/e2freefrag.c \ > + $(srcdir)/filefrag.c > > LIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSS) $(LIBCOM_ERR) $(LIBBLKID) \ > $(LIBUUID) > diff --git a/debugfs/debug_cmds.ct b/debugfs/debug_cmds.ct > index 47de672..af969b1 100644 > --- a/debugfs/debug_cmds.ct > +++ b/debugfs/debug_cmds.ct > @@ -52,6 +52,9 @@ request do_dump_extents, "Dump extents information ", > request do_blocks, "Dump blocks used by an inode ", > blocks; > > +request do_filefrag, "Report fragmentation information for an inode", > + filefrag; > + > request do_link, "Create directory link", > link, ln; > > diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in > index 69490ff..70c8326 100644 > --- a/debugfs/debugfs.8.in > +++ b/debugfs/debugfs.8.in > @@ -251,6 +251,27 @@ Set or clear various filesystem features in the superblock. After setting > or clearing any filesystem features that were requested, print the current > state of the filesystem feature set. > .TP > +.I filefrag [-dvr] filespec > +Print the number of contiguous extents in > +.IR filespec . > +If > +.I filespec > +is a directory and the > +.I -d > +option is not specified, > +.I filefrag > +will print the number of contiguous extents for each file in > +the directory. The > +.I -v > +option will cause > +.I filefrag > +print a tabular listing of the contiguous extents in the > +file. The > +.I -r > +option will cause > +.I filefrag > +to do a recursive listing of the directory. > +.TP > .I find_free_block [count [goal]] > Find the first > .I count > diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h > index 6d7dfcd..0afa1df 100644 > --- a/debugfs/debugfs.h > +++ b/debugfs/debugfs.h > @@ -134,3 +134,4 @@ extern void do_supported_features(int argc, char **argv); > extern void do_punch(int argc, char **argv); > > extern void do_freefrag(int argc, char **argv); > +extern void do_filefrag(int argc, char *argv[]); > diff --git a/debugfs/filefrag.c b/debugfs/filefrag.c > new file mode 100644 > index 0000000..30933b6 > --- /dev/null > +++ b/debugfs/filefrag.c > @@ -0,0 +1,324 @@ > +/* > + * filefrag.c --- display the fragmentation information for a file > + * > + * Copyright (C) 2011 Theodore Ts'o. This file may be redistributed > + * under the terms of the GNU Public License. > + */ > + > +#include "config.h" > +#include <stdio.h> > +#include <unistd.h> > +#include <stdlib.h> > +#include <ctype.h> > +#include <string.h> > +#include <time.h> > +#ifdef HAVE_ERRNO_H > +#include <errno.h> > +#endif > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <fcntl.h> > +#include <utime.h> > +#ifdef HAVE_GETOPT_H > +#include <getopt.h> > +#else > +extern int optind; > +extern char *optarg; > +#endif > + > +#include "debugfs.h" > + > +#define VERBOSE_OPT 0x0001 > +#define DIR_OPT 0x0002 > +#define RECURSIVE_OPT 0x0004 > + > +struct dir_list { > + char *name; > + ext2_ino_t ino; > + struct dir_list *next; > +}; > + > +struct filefrag_struct { > + FILE *f; > + const char *name; > + const char *dir_name; > + int options; > + int logical_width; > + int physical_width; > + int ext; > + int cont_ext; > + e2_blkcnt_t num; > + e2_blkcnt_t logical_start; > + blk64_t physical_start; > + blk64_t expected; > + struct dir_list *dir_list, *dir_last; > +}; > + > +static int int_log10(unsigned long long arg) > +{ > + int l = 0; > + > + arg = arg / 10; > + while (arg) { > + l++; > + arg = arg / 10; > + } > + return l; > +} > + > +static void print_header(struct filefrag_struct *fs) > +{ > + if (fs->options & VERBOSE_OPT) { > + fprintf(fs->f, "%4s %*s %*s %*s %*s\n", "ext", > + fs->logical_width, "logical", fs->physical_width, > + "physical", fs->physical_width, "expected", > + fs->logical_width, "length"); > + } > +} > + > +static void report_filefrag(struct filefrag_struct *fs) > +{ > + if (fs->num == 0) > + return; > + if (fs->options & VERBOSE_OPT) { > + if (fs->expected) > + fprintf(fs->f, "%4d %*lu %*llu %*llu %*lu\n", fs->ext, > + fs->logical_width, > + (unsigned long) fs->logical_start, > + fs->physical_width, fs->physical_start, > + fs->physical_width, fs->expected, > + fs->logical_width, (unsigned long) fs->num); > + else > + fprintf(fs->f, "%4d %*lu %*llu %*s %*lu\n", fs->ext, > + fs->logical_width, > + (unsigned long) fs->logical_start, > + fs->physical_width, fs->physical_start, > + fs->physical_width, "", > + fs->logical_width, (unsigned long) fs->num); > + } > + fs->ext++; > +} > + > +static int filefrag_blocks_proc(ext2_filsys ext4_fs EXT2FS_ATTR((unused)), > + blk64_t *blocknr, e2_blkcnt_t blockcnt, > + blk64_t ref_block EXT2FS_ATTR((unused)), > + int ref_offset EXT2FS_ATTR((unused)), > + void *private) > +{ > + struct filefrag_struct *fs = private; > + > + if (blockcnt < 0 || *blocknr == 0) > + return 0; > + > + if ((fs->num == 0) || (blockcnt != fs->logical_start + fs->num) || > + (*blocknr != fs->physical_start + fs->num)) { > + report_filefrag(fs); > + if (blockcnt == fs->logical_start + fs->num) > + fs->expected = fs->physical_start + fs->num; > + else > + fs->expected = 0; > + fs->logical_start = blockcnt; > + fs->physical_start = *blocknr; > + fs->num = 1; > + fs->cont_ext++; > + } else > + fs->num++; > + return 0; > +} > + > +static void filefrag(ext2_ino_t ino, struct ext2_inode *inode, > + struct filefrag_struct *fs) > +{ > + errcode_t retval; > + int blocksize = current_fs->blocksize; > + > + fs->logical_width = int_log10((EXT2_I_SIZE(inode) + blocksize - 1) / > + blocksize) + 1; > + if (fs->logical_width < 7) > + fs->logical_width = 7; > + fs->ext = 0; > + fs->cont_ext = 0; > + fs->logical_start = 0; > + fs->physical_start = 0; > + fs->num = 0; > + > + if (fs->options & VERBOSE_OPT) { > + blk64_t num_blocks = ext2fs_inode_i_blocks(current_fs, inode); > + > + if (!(current_fs->super->s_feature_ro_compat & > + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || > + !(inode->i_flags & EXT4_HUGE_FILE_FL)) > + num_blocks /= current_fs->blocksize / 512; > + > + fprintf(fs->f, "\n%s has %llu block(s), i_size is %llu\n", > + fs->name, num_blocks, EXT2_I_SIZE(inode)); > + } > + print_header(fs); > + retval = ext2fs_block_iterate3(current_fs, ino, > + BLOCK_FLAG_READ_ONLY, NULL, > + filefrag_blocks_proc, fs); > + if (retval) > + com_err("ext2fs_block_iterate3", retval, 0); > + > + report_filefrag(fs); > + fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext, > + LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : ""); > +} > + > +static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), > + int entry, > + struct ext2_dir_entry *dirent, > + int offset EXT2FS_ATTR((unused)), > + int blocksize EXT2FS_ATTR((unused)), > + char *buf EXT2FS_ATTR((unused)), > + void *private) > +{ > + struct filefrag_struct *fs = private; > + struct ext2_inode inode; > + ext2_ino_t ino; > + char name[EXT2_NAME_LEN + 1]; > + char *cp; > + int thislen; > + > + if (entry == DIRENT_DELETED_FILE) > + return 0; > + > + thislen = dirent->name_len & 0xFF; > + strncpy(name, dirent->name, thislen); > + name[thislen] = '\0'; > + ino = dirent->inode; > + > + if (!strcmp(name, ".") || !strcmp(name, "..")) > + return 0; > + > + cp = malloc(strlen(fs->dir_name) + strlen(name) + 2); > + if (!cp) { > + fprintf(stderr, "Couldn't allocate memory for %s/%s\n", > + fs->dir_name, name); > + return 0; > + } > + > + sprintf(cp, "%s/%s", fs->dir_name, name); > + fs->name = cp; > + > + if (debugfs_read_inode(ino, &inode, fs->name)) > + goto errout; > + > + filefrag(ino, &inode, fs); > + > + if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) { > + struct dir_list *p; > + > + p = malloc(sizeof(struct dir_list)); > + if (!p) { > + fprintf(stderr, "Couldn't allocate dir_list for %s\n", > + fs->name); > + goto errout; > + } > + memset(p, 0, sizeof(struct dir_list)); > + p->name = cp; > + p->ino = ino; > + if (fs->dir_last) > + fs->dir_last->next = p; > + else > + fs->dir_list = p; > + fs->dir_last = p; > + return 0; > + } > +errout: > + free(cp); > + fs->name = 0; > + return 0; > +} > + > + > +static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs) > +{ > + errcode_t retval; > + struct dir_list *p = NULL; > + > + fs->dir_name = fs->name; > + > + while (1) { > + retval = ext2fs_dir_iterate2(current_fs, ino, 0, > + 0, filefrag_dir_proc, fs); > + if (retval) > + com_err("ext2fs_dir_iterate2", retval, 0); > + if (p) { > + free(p->name); > + fs->dir_list = p->next; > + if (!fs->dir_list) > + fs->dir_last = 0; > + free(p); > + } > + p = fs->dir_list; > + if (!p) > + break; > + ino = p->ino; > + fs->dir_name = p->name; > + } > +} > + > +void do_filefrag(int argc, char *argv[]) > +{ > + struct filefrag_struct fs; > + struct ext2_inode inode; > + ext2_ino_t ino; > + int c; > + > + memset(&fs, 0, sizeof(fs)); > + if (check_fs_open(argv[0])) > + return; > + > + reset_getopt(); > + while ((c = getopt (argc, argv, "dvr")) != EOF) { > + switch (c) { > + case 'd': > + fs.options |= DIR_OPT; > + break; > + case 'v': > + fs.options |= VERBOSE_OPT; > + break; > + case 'r': > + fs.options |= RECURSIVE_OPT; > + break; > + default: > + goto print_usage; > + } > + } > + > + if (argc > optind+1) { > + print_usage: > + com_err(0, 0, "Usage: filefrag [-dv] file"); > + return; > + } > + > + if (argc == optind) { > + ino = cwd; > + fs.name = "."; > + } else { > + ino = string_to_inode(argv[optind]); > + fs.name = argv[optind]; > + } > + if (!ino) > + return; > + > + if (debugfs_read_inode(ino, &inode, argv[0])) > + return; > + > + fs.f = open_pager(); > + fs.physical_width = int_log10(ext2fs_blocks_count(current_fs->super)); > + fs.physical_width++; > + if (fs.physical_width < 8) > + fs.physical_width = 8; > + > + if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT)) > + filefrag(ino, &inode, &fs); > + else > + dir_iterate(ino, &fs); > + > + fprintf(fs.f, "\n"); > + close_pager(fs.f); > + > + return; > +} > diff --git a/debugfs/ro_debug_cmds.ct b/debugfs/ro_debug_cmds.ct > index 7eb552d..4feb621 100644 > --- a/debugfs/ro_debug_cmds.ct > +++ b/debugfs/ro_debug_cmds.ct > @@ -45,6 +45,9 @@ request do_dump_extents, "Dump extents information ", > request do_blocks, "Dump blocks used by an inode ", > blocks; > > +request do_filefrag, "Report fragmentation information for an inode", > + filefrag; > + > request do_testi, "Test an inode's in-use flag", > testi; > -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Nov 17, 2011, at 4:19 PM, Eric Sandeen wrote: > On 11/17/11 3:12 PM, Theodore Ts'o wrote: >> Add the ability to report on the fragmentation of a file on a file >> system opened using debugfs. >> >> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> >> --- >> debugfs/Makefile.in | 8 +- >> debugfs/debug_cmds.ct | 3 + >> debugfs/debugfs.8.in | 21 +++ >> debugfs/debugfs.h | 1 + >> debugfs/filefrag.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++ > > Is it possible to share any of ^^^ that code ^^^ with misc/filefrag.c somehow? Well, the debugs code accesses the file system directly; I thought about creating glue code that created an interface similar to FIEMAP ioctl, but the glue code ends up being more than the code to do just do the fragmentation calculation. -- Ted -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/debugfs/Makefile.in b/debugfs/Makefile.in index e03a3c6..c6aaa3a 100644 --- a/debugfs/Makefile.in +++ b/debugfs/Makefile.in @@ -17,15 +17,17 @@ MANPAGES= debugfs.8 MK_CMDS= _SS_DIR_OVERRIDE=../lib/ss ../lib/ss/mk_cmds DEBUG_OBJS= debug_cmds.o debugfs.o util.o ncheck.o icheck.o ls.o \ - lsdel.o dump.o set_fields.o logdump.o htree.o unused.o e2freefrag.o + lsdel.o dump.o set_fields.o logdump.o htree.o unused.o e2freefrag.o \ + filefrag.o RO_DEBUG_OBJS= ro_debug_cmds.o ro_debugfs.o util.o ncheck.o icheck.o ls.o \ - lsdel.o logdump.o htree.o e2freefrag.o + lsdel.o logdump.o htree.o e2freefrag.o filefrag.o SRCS= debug_cmds.c $(srcdir)/debugfs.c $(srcdir)/util.c $(srcdir)/ls.c \ $(srcdir)/ncheck.c $(srcdir)/icheck.c $(srcdir)/lsdel.c \ $(srcdir)/dump.c $(srcdir)/set_fields.c ${srcdir}/logdump.c \ - $(srcdir)/htree.c $(srcdir)/unused.c + $(srcdir)/htree.c $(srcdir)/unused.c ${srcdir}/../misc/e2freefrag.c \ + $(srcdir)/filefrag.c LIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBSS) $(LIBCOM_ERR) $(LIBBLKID) \ $(LIBUUID) diff --git a/debugfs/debug_cmds.ct b/debugfs/debug_cmds.ct index 47de672..af969b1 100644 --- a/debugfs/debug_cmds.ct +++ b/debugfs/debug_cmds.ct @@ -52,6 +52,9 @@ request do_dump_extents, "Dump extents information ", request do_blocks, "Dump blocks used by an inode ", blocks; +request do_filefrag, "Report fragmentation information for an inode", + filefrag; + request do_link, "Create directory link", link, ln; diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in index 69490ff..70c8326 100644 --- a/debugfs/debugfs.8.in +++ b/debugfs/debugfs.8.in @@ -251,6 +251,27 @@ Set or clear various filesystem features in the superblock. After setting or clearing any filesystem features that were requested, print the current state of the filesystem feature set. .TP +.I filefrag [-dvr] filespec +Print the number of contiguous extents in +.IR filespec . +If +.I filespec +is a directory and the +.I -d +option is not specified, +.I filefrag +will print the number of contiguous extents for each file in +the directory. The +.I -v +option will cause +.I filefrag +print a tabular listing of the contiguous extents in the +file. The +.I -r +option will cause +.I filefrag +to do a recursive listing of the directory. +.TP .I find_free_block [count [goal]] Find the first .I count diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h index 6d7dfcd..0afa1df 100644 --- a/debugfs/debugfs.h +++ b/debugfs/debugfs.h @@ -134,3 +134,4 @@ extern void do_supported_features(int argc, char **argv); extern void do_punch(int argc, char **argv); extern void do_freefrag(int argc, char **argv); +extern void do_filefrag(int argc, char *argv[]); diff --git a/debugfs/filefrag.c b/debugfs/filefrag.c new file mode 100644 index 0000000..30933b6 --- /dev/null +++ b/debugfs/filefrag.c @@ -0,0 +1,324 @@ +/* + * filefrag.c --- display the fragmentation information for a file + * + * Copyright (C) 2011 Theodore Ts'o. This file may be redistributed + * under the terms of the GNU Public License. + */ + +#include "config.h" +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <utime.h> +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern int optind; +extern char *optarg; +#endif + +#include "debugfs.h" + +#define VERBOSE_OPT 0x0001 +#define DIR_OPT 0x0002 +#define RECURSIVE_OPT 0x0004 + +struct dir_list { + char *name; + ext2_ino_t ino; + struct dir_list *next; +}; + +struct filefrag_struct { + FILE *f; + const char *name; + const char *dir_name; + int options; + int logical_width; + int physical_width; + int ext; + int cont_ext; + e2_blkcnt_t num; + e2_blkcnt_t logical_start; + blk64_t physical_start; + blk64_t expected; + struct dir_list *dir_list, *dir_last; +}; + +static int int_log10(unsigned long long arg) +{ + int l = 0; + + arg = arg / 10; + while (arg) { + l++; + arg = arg / 10; + } + return l; +} + +static void print_header(struct filefrag_struct *fs) +{ + if (fs->options & VERBOSE_OPT) { + fprintf(fs->f, "%4s %*s %*s %*s %*s\n", "ext", + fs->logical_width, "logical", fs->physical_width, + "physical", fs->physical_width, "expected", + fs->logical_width, "length"); + } +} + +static void report_filefrag(struct filefrag_struct *fs) +{ + if (fs->num == 0) + return; + if (fs->options & VERBOSE_OPT) { + if (fs->expected) + fprintf(fs->f, "%4d %*lu %*llu %*llu %*lu\n", fs->ext, + fs->logical_width, + (unsigned long) fs->logical_start, + fs->physical_width, fs->physical_start, + fs->physical_width, fs->expected, + fs->logical_width, (unsigned long) fs->num); + else + fprintf(fs->f, "%4d %*lu %*llu %*s %*lu\n", fs->ext, + fs->logical_width, + (unsigned long) fs->logical_start, + fs->physical_width, fs->physical_start, + fs->physical_width, "", + fs->logical_width, (unsigned long) fs->num); + } + fs->ext++; +} + +static int filefrag_blocks_proc(ext2_filsys ext4_fs EXT2FS_ATTR((unused)), + blk64_t *blocknr, e2_blkcnt_t blockcnt, + blk64_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *private) +{ + struct filefrag_struct *fs = private; + + if (blockcnt < 0 || *blocknr == 0) + return 0; + + if ((fs->num == 0) || (blockcnt != fs->logical_start + fs->num) || + (*blocknr != fs->physical_start + fs->num)) { + report_filefrag(fs); + if (blockcnt == fs->logical_start + fs->num) + fs->expected = fs->physical_start + fs->num; + else + fs->expected = 0; + fs->logical_start = blockcnt; + fs->physical_start = *blocknr; + fs->num = 1; + fs->cont_ext++; + } else + fs->num++; + return 0; +} + +static void filefrag(ext2_ino_t ino, struct ext2_inode *inode, + struct filefrag_struct *fs) +{ + errcode_t retval; + int blocksize = current_fs->blocksize; + + fs->logical_width = int_log10((EXT2_I_SIZE(inode) + blocksize - 1) / + blocksize) + 1; + if (fs->logical_width < 7) + fs->logical_width = 7; + fs->ext = 0; + fs->cont_ext = 0; + fs->logical_start = 0; + fs->physical_start = 0; + fs->num = 0; + + if (fs->options & VERBOSE_OPT) { + blk64_t num_blocks = ext2fs_inode_i_blocks(current_fs, inode); + + if (!(current_fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || + !(inode->i_flags & EXT4_HUGE_FILE_FL)) + num_blocks /= current_fs->blocksize / 512; + + fprintf(fs->f, "\n%s has %llu block(s), i_size is %llu\n", + fs->name, num_blocks, EXT2_I_SIZE(inode)); + } + print_header(fs); + retval = ext2fs_block_iterate3(current_fs, ino, + BLOCK_FLAG_READ_ONLY, NULL, + filefrag_blocks_proc, fs); + if (retval) + com_err("ext2fs_block_iterate3", retval, 0); + + report_filefrag(fs); + fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext, + LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : ""); +} + +static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), + int entry, + struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *private) +{ + struct filefrag_struct *fs = private; + struct ext2_inode inode; + ext2_ino_t ino; + char name[EXT2_NAME_LEN + 1]; + char *cp; + int thislen; + + if (entry == DIRENT_DELETED_FILE) + return 0; + + thislen = dirent->name_len & 0xFF; + strncpy(name, dirent->name, thislen); + name[thislen] = '\0'; + ino = dirent->inode; + + if (!strcmp(name, ".") || !strcmp(name, "..")) + return 0; + + cp = malloc(strlen(fs->dir_name) + strlen(name) + 2); + if (!cp) { + fprintf(stderr, "Couldn't allocate memory for %s/%s\n", + fs->dir_name, name); + return 0; + } + + sprintf(cp, "%s/%s", fs->dir_name, name); + fs->name = cp; + + if (debugfs_read_inode(ino, &inode, fs->name)) + goto errout; + + filefrag(ino, &inode, fs); + + if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) { + struct dir_list *p; + + p = malloc(sizeof(struct dir_list)); + if (!p) { + fprintf(stderr, "Couldn't allocate dir_list for %s\n", + fs->name); + goto errout; + } + memset(p, 0, sizeof(struct dir_list)); + p->name = cp; + p->ino = ino; + if (fs->dir_last) + fs->dir_last->next = p; + else + fs->dir_list = p; + fs->dir_last = p; + return 0; + } +errout: + free(cp); + fs->name = 0; + return 0; +} + + +static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs) +{ + errcode_t retval; + struct dir_list *p = NULL; + + fs->dir_name = fs->name; + + while (1) { + retval = ext2fs_dir_iterate2(current_fs, ino, 0, + 0, filefrag_dir_proc, fs); + if (retval) + com_err("ext2fs_dir_iterate2", retval, 0); + if (p) { + free(p->name); + fs->dir_list = p->next; + if (!fs->dir_list) + fs->dir_last = 0; + free(p); + } + p = fs->dir_list; + if (!p) + break; + ino = p->ino; + fs->dir_name = p->name; + } +} + +void do_filefrag(int argc, char *argv[]) +{ + struct filefrag_struct fs; + struct ext2_inode inode; + ext2_ino_t ino; + int c; + + memset(&fs, 0, sizeof(fs)); + if (check_fs_open(argv[0])) + return; + + reset_getopt(); + while ((c = getopt (argc, argv, "dvr")) != EOF) { + switch (c) { + case 'd': + fs.options |= DIR_OPT; + break; + case 'v': + fs.options |= VERBOSE_OPT; + break; + case 'r': + fs.options |= RECURSIVE_OPT; + break; + default: + goto print_usage; + } + } + + if (argc > optind+1) { + print_usage: + com_err(0, 0, "Usage: filefrag [-dv] file"); + return; + } + + if (argc == optind) { + ino = cwd; + fs.name = "."; + } else { + ino = string_to_inode(argv[optind]); + fs.name = argv[optind]; + } + if (!ino) + return; + + if (debugfs_read_inode(ino, &inode, argv[0])) + return; + + fs.f = open_pager(); + fs.physical_width = int_log10(ext2fs_blocks_count(current_fs->super)); + fs.physical_width++; + if (fs.physical_width < 8) + fs.physical_width = 8; + + if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT)) + filefrag(ino, &inode, &fs); + else + dir_iterate(ino, &fs); + + fprintf(fs.f, "\n"); + close_pager(fs.f); + + return; +} diff --git a/debugfs/ro_debug_cmds.ct b/debugfs/ro_debug_cmds.ct index 7eb552d..4feb621 100644 --- a/debugfs/ro_debug_cmds.ct +++ b/debugfs/ro_debug_cmds.ct @@ -45,6 +45,9 @@ request do_dump_extents, "Dump extents information ", request do_blocks, "Dump blocks used by an inode ", blocks; +request do_filefrag, "Report fragmentation information for an inode", + filefrag; + request do_testi, "Test an inode's in-use flag", testi;
Add the ability to report on the fragmentation of a file on a file system opened using debugfs. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> --- debugfs/Makefile.in | 8 +- debugfs/debug_cmds.ct | 3 + debugfs/debugfs.8.in | 21 +++ debugfs/debugfs.h | 1 + debugfs/filefrag.c | 324 ++++++++++++++++++++++++++++++++++++++++++++++ debugfs/ro_debug_cmds.ct | 3 + 6 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 debugfs/filefrag.c