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,
88 fs->physical_width, fs->physical_start,
89 fs->physical_width, fs->expected,
90 fs->logical_width, (unsigned long) fs->num);
92 fprintf(fs->f, "%4d %*lu %*llu %*s %*lu\n", fs->ext,
94 (unsigned long) fs->logical_start,
95 fs->physical_width, fs->physical_start,
96 fs->physical_width, "",
97 fs->logical_width, (unsigned long) fs->num);
102 static int filefrag_blocks_proc(ext2_filsys ext4_fs EXT2FS_ATTR((unused)),
103 blk64_t *blocknr, e2_blkcnt_t blockcnt,
104 blk64_t ref_block EXT2FS_ATTR((unused)),
105 int ref_offset EXT2FS_ATTR((unused)),
108 struct filefrag_struct *fs = private;
110 if (blockcnt < 0 || *blocknr == 0)
113 if ((fs->num == 0) || (blockcnt != fs->logical_start + fs->num) ||
114 (*blocknr != fs->physical_start + fs->num)) {
116 if (blockcnt == fs->logical_start + fs->num)
117 fs->expected = fs->physical_start + fs->num;
120 fs->logical_start = blockcnt;
121 fs->physical_start = *blocknr;
129 static void filefrag(ext2_ino_t ino, struct ext2_inode *inode,
130 struct filefrag_struct *fs)
133 int blocksize = current_fs->blocksize;
135 fs->logical_width = int_log10((EXT2_I_SIZE(inode) + blocksize - 1) /
137 if (fs->logical_width < 7)
138 fs->logical_width = 7;
141 fs->logical_start = 0;
142 fs->physical_start = 0;
145 if (fs->options & VERBOSE_OPT) {
146 blk64_t num_blocks = ext2fs_inode_i_blocks(current_fs, inode);
148 if (!ext2fs_has_feature_huge_file(current_fs->super) ||
149 !(inode->i_flags & EXT4_HUGE_FILE_FL))
150 num_blocks /= current_fs->blocksize / 512;
152 fprintf(fs->f, "\n%s has %llu block(s), i_size is %llu\n",
153 fs->name, num_blocks, EXT2_I_SIZE(inode));
156 if (ext2fs_inode_has_valid_blocks2(current_fs, inode)) {
157 retval = ext2fs_block_iterate3(current_fs, ino,
158 BLOCK_FLAG_READ_ONLY, NULL,
159 filefrag_blocks_proc, fs);
161 com_err("ext2fs_block_iterate3", retval, 0);
165 fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
166 LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : "");
169 static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
171 struct ext2_dir_entry *dirent,
172 int offset EXT2FS_ATTR((unused)),
173 int blocksize EXT2FS_ATTR((unused)),
174 char *buf EXT2FS_ATTR((unused)),
177 struct filefrag_struct *fs = private;
178 struct ext2_inode inode;
180 char name[EXT2_NAME_LEN + 1];
184 if (entry == DIRENT_DELETED_FILE)
187 thislen = ext2fs_dirent_name_len(dirent);
188 strncpy(name, dirent->name, thislen);
189 name[thislen] = '\0';
192 if (!strcmp(name, ".") || !strcmp(name, ".."))
195 cp = malloc(strlen(fs->dir_name) + strlen(name) + 2);
197 fprintf(stderr, "Couldn't allocate memory for %s/%s\n",
202 sprintf(cp, "%s/%s", fs->dir_name, name);
205 if (debugfs_read_inode(ino, &inode, fs->name))
208 filefrag(ino, &inode, fs);
210 if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) {
213 p = malloc(sizeof(struct dir_list));
215 fprintf(stderr, "Couldn't allocate dir_list for %s\n",
219 memset(p, 0, sizeof(struct dir_list));
223 fs->dir_last->next = p;
236 static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs)
239 struct dir_list *p = NULL;
241 fs->dir_name = fs->name;
244 retval = ext2fs_dir_iterate2(current_fs, ino, 0,
245 0, filefrag_dir_proc, fs);
247 com_err("ext2fs_dir_iterate2", retval, 0);
250 fs->dir_list = p->next;
259 fs->dir_name = p->name;
263 void do_filefrag(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
264 void *infop EXT2FS_ATTR((unused)))
266 struct filefrag_struct fs;
267 struct ext2_inode inode;
271 memset(&fs, 0, sizeof(fs));
272 if (check_fs_open(argv[0]))
276 while ((c = getopt(argc, argv, "dvr")) != EOF) {
279 fs.options |= DIR_OPT;
282 fs.options |= VERBOSE_OPT;
285 fs.options |= RECURSIVE_OPT;
292 if (argc > optind+1) {
294 com_err(0, 0, "Usage: filefrag [-dvr] file");
298 if (argc == optind) {
302 ino = string_to_inode(argv[optind]);
303 fs.name = argv[optind];
308 if (debugfs_read_inode(ino, &inode, argv[0]))
312 fs.physical_width = int_log10(ext2fs_blocks_count(current_fs->super));
314 if (fs.physical_width < 8)
315 fs.physical_width = 8;
317 if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT))
318 filefrag(ino, &inode, &fs);
320 dir_iterate(ino, &fs);