2 * filefrag.c --- display the fragmentation information for a file
4 * Copyright (C) 2011 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
18 #include <sys/types.h>
31 #define VERBOSE_OPT 0x0001
32 #define DIR_OPT 0x0002
33 #define RECURSIVE_OPT 0x0004
38 struct dir_list *next;
41 struct filefrag_struct {
51 e2_blkcnt_t logical_start;
52 blk64_t physical_start;
54 struct dir_list *dir_list, *dir_last;
57 static void print_header(struct filefrag_struct *fs)
59 if (fs->options & VERBOSE_OPT) {
60 fprintf(fs->f, "%4s %*s %*s %*s %*s\n", "ext",
61 fs->logical_width, "logical", fs->physical_width,
62 "physical", fs->physical_width, "expected",
63 fs->logical_width, "length");
67 static void report_filefrag(struct filefrag_struct *fs)
71 if (fs->options & VERBOSE_OPT) {
73 fprintf(fs->f, "%4d %*lu %*llu %*llu %*lu\n", fs->ext,
75 (unsigned long) fs->logical_start,
77 (unsigned long long) fs->physical_start,
79 (unsigned long long) fs->expected,
80 fs->logical_width, (unsigned long) fs->num);
82 fprintf(fs->f, "%4d %*lu %*llu %*s %*lu\n", fs->ext,
84 (unsigned long) fs->logical_start,
86 (unsigned long long) fs->physical_start,
87 fs->physical_width, "",
88 fs->logical_width, (unsigned long) fs->num);
93 static int filefrag_blocks_proc(ext2_filsys ext4_fs EXT2FS_ATTR((unused)),
94 blk64_t *blocknr, e2_blkcnt_t blockcnt,
95 blk64_t ref_block EXT2FS_ATTR((unused)),
96 int ref_offset EXT2FS_ATTR((unused)),
99 struct filefrag_struct *fs = private;
101 if (blockcnt < 0 || *blocknr == 0)
104 if ((fs->num == 0) || (blockcnt != fs->logical_start + fs->num) ||
105 (*blocknr != fs->physical_start + fs->num)) {
107 if (blockcnt == fs->logical_start + fs->num)
108 fs->expected = fs->physical_start + fs->num;
111 fs->logical_start = blockcnt;
112 fs->physical_start = *blocknr;
120 static void filefrag(ext2_ino_t ino, struct ext2_inode *inode,
121 struct filefrag_struct *fs)
124 int blocksize = current_fs->blocksize;
126 fs->logical_width = ext2fs_log10((EXT2_I_SIZE(inode) + blocksize - 1) /
128 if (fs->logical_width < 7)
129 fs->logical_width = 7;
132 fs->logical_start = 0;
133 fs->physical_start = 0;
136 if (fs->options & VERBOSE_OPT) {
137 blk64_t num_blocks = ext2fs_inode_i_blocks(current_fs, inode);
139 if (!ext2fs_has_feature_huge_file(current_fs->super) ||
140 !(inode->i_flags & EXT4_HUGE_FILE_FL))
141 num_blocks /= current_fs->blocksize / 512;
143 fprintf(fs->f, "\n%s has %llu block(s), i_size is %llu\n",
144 fs->name, (unsigned long long) num_blocks,
145 (unsigned long long) EXT2_I_SIZE(inode));
148 if (ext2fs_inode_has_valid_blocks2(current_fs, inode)) {
149 retval = ext2fs_block_iterate3(current_fs, ino,
150 BLOCK_FLAG_READ_ONLY, NULL,
151 filefrag_blocks_proc, fs);
153 com_err("ext2fs_block_iterate3", retval, 0);
157 fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
158 LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : "");
161 static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
163 struct ext2_dir_entry *dirent,
164 int offset EXT2FS_ATTR((unused)),
165 int blocksize EXT2FS_ATTR((unused)),
166 char *buf EXT2FS_ATTR((unused)),
169 struct filefrag_struct *fs = private;
170 struct ext2_inode inode;
172 char name[EXT2_NAME_LEN + 1];
176 if (entry == DIRENT_DELETED_FILE)
179 thislen = ext2fs_dirent_name_len(dirent);
180 strncpy(name, dirent->name, thislen);
181 name[thislen] = '\0';
184 if (!strcmp(name, ".") || !strcmp(name, ".."))
187 cp = malloc(strlen(fs->dir_name) + strlen(name) + 2);
189 fprintf(stderr, "Couldn't allocate memory for %s/%s\n",
194 sprintf(cp, "%s/%s", fs->dir_name, name);
197 if (debugfs_read_inode(ino, &inode, fs->name))
200 filefrag(ino, &inode, fs);
202 if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) {
205 p = malloc(sizeof(struct dir_list));
207 fprintf(stderr, "Couldn't allocate dir_list for %s\n",
211 memset(p, 0, sizeof(struct dir_list));
215 fs->dir_last->next = p;
228 static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs)
231 struct dir_list *p = NULL;
233 fs->dir_name = fs->name;
236 retval = ext2fs_dir_iterate2(current_fs, ino, 0,
237 0, filefrag_dir_proc, fs);
239 com_err("ext2fs_dir_iterate2", retval, 0);
242 fs->dir_list = p->next;
251 fs->dir_name = p->name;
255 void do_filefrag(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
256 void *infop EXT2FS_ATTR((unused)))
258 struct filefrag_struct fs;
259 struct ext2_inode inode;
263 memset(&fs, 0, sizeof(fs));
264 if (check_fs_open(argv[0]))
268 while ((c = getopt(argc, argv, "dvr")) != EOF) {
271 fs.options |= DIR_OPT;
274 fs.options |= VERBOSE_OPT;
277 fs.options |= RECURSIVE_OPT;
284 if (argc > optind+1) {
286 com_err(0, 0, "Usage: filefrag [-dvr] file");
290 if (argc == optind) {
294 ino = string_to_inode(argv[optind]);
295 fs.name = argv[optind];
300 if (debugfs_read_inode(ino, &inode, argv[0]))
304 fs.physical_width = ext2fs_log10(ext2fs_blocks_count(current_fs->super));
306 if (fs.physical_width < 8)
307 fs.physical_width = 8;
309 if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT))
310 filefrag(ino, &inode, &fs);
312 dir_iterate(ino, &fs);