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 int int_log10(unsigned long long arg)
69 static void print_header(struct filefrag_struct *fs)
71 if (fs->options & VERBOSE_OPT) {
72 fprintf(fs->f, "%4s %*s %*s %*s %*s\n", "ext",
73 fs->logical_width, "logical", fs->physical_width,
74 "physical", fs->physical_width, "expected",
75 fs->logical_width, "length");
79 static void report_filefrag(struct filefrag_struct *fs)
83 if (fs->options & VERBOSE_OPT) {
85 fprintf(fs->f, "%4d %*lu %*llu %*llu %*lu\n", fs->ext,
87 (unsigned long) fs->logical_start,
89 (unsigned long long) fs->physical_start,
91 (unsigned long long) fs->expected,
92 fs->logical_width, (unsigned long) fs->num);
94 fprintf(fs->f, "%4d %*lu %*llu %*s %*lu\n", fs->ext,
96 (unsigned long) fs->logical_start,
98 (unsigned long long) fs->physical_start,
99 fs->physical_width, "",
100 fs->logical_width, (unsigned long) fs->num);
105 static int filefrag_blocks_proc(ext2_filsys ext4_fs EXT2FS_ATTR((unused)),
106 blk64_t *blocknr, e2_blkcnt_t blockcnt,
107 blk64_t ref_block EXT2FS_ATTR((unused)),
108 int ref_offset EXT2FS_ATTR((unused)),
111 struct filefrag_struct *fs = private;
113 if (blockcnt < 0 || *blocknr == 0)
116 if ((fs->num == 0) || (blockcnt != fs->logical_start + fs->num) ||
117 (*blocknr != fs->physical_start + fs->num)) {
119 if (blockcnt == fs->logical_start + fs->num)
120 fs->expected = fs->physical_start + fs->num;
123 fs->logical_start = blockcnt;
124 fs->physical_start = *blocknr;
132 static void filefrag(ext2_ino_t ino, struct ext2_inode *inode,
133 struct filefrag_struct *fs)
136 int blocksize = current_fs->blocksize;
138 fs->logical_width = int_log10((EXT2_I_SIZE(inode) + blocksize - 1) /
140 if (fs->logical_width < 7)
141 fs->logical_width = 7;
144 fs->logical_start = 0;
145 fs->physical_start = 0;
148 if (fs->options & VERBOSE_OPT) {
149 blk64_t num_blocks = ext2fs_inode_i_blocks(current_fs, inode);
151 if (!ext2fs_has_feature_huge_file(current_fs->super) ||
152 !(inode->i_flags & EXT4_HUGE_FILE_FL))
153 num_blocks /= current_fs->blocksize / 512;
155 fprintf(fs->f, "\n%s has %llu block(s), i_size is %llu\n",
156 fs->name, (unsigned long long) num_blocks,
157 (unsigned long long) EXT2_I_SIZE(inode));
160 if (ext2fs_inode_has_valid_blocks2(current_fs, inode)) {
161 retval = ext2fs_block_iterate3(current_fs, ino,
162 BLOCK_FLAG_READ_ONLY, NULL,
163 filefrag_blocks_proc, fs);
165 com_err("ext2fs_block_iterate3", retval, 0);
169 fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
170 LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : "");
173 static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
175 struct ext2_dir_entry *dirent,
176 int offset EXT2FS_ATTR((unused)),
177 int blocksize EXT2FS_ATTR((unused)),
178 char *buf EXT2FS_ATTR((unused)),
181 struct filefrag_struct *fs = private;
182 struct ext2_inode inode;
184 char name[EXT2_NAME_LEN + 1];
188 if (entry == DIRENT_DELETED_FILE)
191 thislen = ext2fs_dirent_name_len(dirent);
192 strncpy(name, dirent->name, thislen);
193 name[thislen] = '\0';
196 if (!strcmp(name, ".") || !strcmp(name, ".."))
199 cp = malloc(strlen(fs->dir_name) + strlen(name) + 2);
201 fprintf(stderr, "Couldn't allocate memory for %s/%s\n",
206 sprintf(cp, "%s/%s", fs->dir_name, name);
209 if (debugfs_read_inode(ino, &inode, fs->name))
212 filefrag(ino, &inode, fs);
214 if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) {
217 p = malloc(sizeof(struct dir_list));
219 fprintf(stderr, "Couldn't allocate dir_list for %s\n",
223 memset(p, 0, sizeof(struct dir_list));
227 fs->dir_last->next = p;
240 static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs)
243 struct dir_list *p = NULL;
245 fs->dir_name = fs->name;
248 retval = ext2fs_dir_iterate2(current_fs, ino, 0,
249 0, filefrag_dir_proc, fs);
251 com_err("ext2fs_dir_iterate2", retval, 0);
254 fs->dir_list = p->next;
263 fs->dir_name = p->name;
267 void do_filefrag(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
268 void *infop EXT2FS_ATTR((unused)))
270 struct filefrag_struct fs;
271 struct ext2_inode inode;
275 memset(&fs, 0, sizeof(fs));
276 if (check_fs_open(argv[0]))
280 while ((c = getopt(argc, argv, "dvr")) != EOF) {
283 fs.options |= DIR_OPT;
286 fs.options |= VERBOSE_OPT;
289 fs.options |= RECURSIVE_OPT;
296 if (argc > optind+1) {
298 com_err(0, 0, "Usage: filefrag [-dvr] file");
302 if (argc == optind) {
306 ino = string_to_inode(argv[optind]);
307 fs.name = argv[optind];
312 if (debugfs_read_inode(ino, &inode, argv[0]))
316 fs.physical_width = int_log10(ext2fs_blocks_count(current_fs->super));
318 if (fs.physical_width < 8)
319 fs.physical_width = 8;
321 if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT))
322 filefrag(ino, &inode, &fs);
324 dir_iterate(ino, &fs);