diff options
Diffstat (limited to 'debugfs/filefrag.c')
-rw-r--r-- | debugfs/filefrag.c | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/debugfs/filefrag.c b/debugfs/filefrag.c new file mode 100644 index 0000000..31c1440 --- /dev/null +++ b/debugfs/filefrag.c @@ -0,0 +1,330 @@ +/* + * 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, + (unsigned long long) fs->physical_start, + fs->physical_width, + (unsigned long long) 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, + (unsigned long long) 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 (!ext2fs_has_feature_huge_file(current_fs->super) || + !(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, (unsigned long long) num_blocks, + (unsigned long long) EXT2_I_SIZE(inode)); + } + print_header(fs); + if (ext2fs_inode_has_valid_blocks2(current_fs, inode)) { + 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 = ext2fs_dirent_name_len(dirent); + 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[], int sci_idx EXT2FS_ATTR((unused)), + void *infop EXT2FS_ATTR((unused))) +{ + 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 [-dvr] 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; +} |