summaryrefslogtreecommitdiffstats
path: root/debugfs/filefrag.c
diff options
context:
space:
mode:
Diffstat (limited to 'debugfs/filefrag.c')
-rw-r--r--debugfs/filefrag.c330
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;
+}