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
);