1 /*
2 * filefrag.c --- display the fragmentation information for a file
3 *
4 * Copyright (C) 2011 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <ctype.h>
12 #include <string.h>
13 #include <time.h>
14 #ifdef HAVE_ERRNO_H
15 #include <errno.h>
16 #endif
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <utime.h>
21 #ifdef HAVE_GETOPT_H
22 #include <getopt.h>
23 #else
24 extern int optind;
25 extern char *optarg;
26 #endif
27
28 #include "debugfs.h"
29
30 #define VERBOSE_OPT 0x0001
31 #define DIR_OPT 0x0002
32 #define RECURSIVE_OPT 0x0004
33
34 struct dir_list {
35 char *name;
36 ext2_ino_t ino;
37 struct dir_list *next;
38 };
39
40 struct filefrag_struct {
41 FILE *f;
42 const char *name;
43 const char *dir_name;
44 int options;
45 int logical_width;
46 int physical_width;
47 int ext;
48 int cont_ext;
49 e2_blkcnt_t num;
50 e2_blkcnt_t logical_start;
51 blk64_t physical_start;
52 blk64_t expected;
53 struct dir_list *dir_list, *dir_last;
54 };
55
int_log10(unsigned long long arg)56 static int int_log10(unsigned long long arg)
57 {
58 int l = 0;
59
60 arg = arg / 10;
61 while (arg) {
62 l++;
63 arg = arg / 10;
64 }
65 return l;
66 }
67
print_header(struct filefrag_struct * fs)68 static void print_header(struct filefrag_struct *fs)
69 {
70 if (fs->options & VERBOSE_OPT) {
71 fprintf(fs->f, "%4s %*s %*s %*s %*s\n", "ext",
72 fs->logical_width, "logical", fs->physical_width,
73 "physical", fs->physical_width, "expected",
74 fs->logical_width, "length");
75 }
76 }
77
report_filefrag(struct filefrag_struct * fs)78 static void report_filefrag(struct filefrag_struct *fs)
79 {
80 if (fs->num == 0)
81 return;
82 if (fs->options & VERBOSE_OPT) {
83 if (fs->expected)
84 fprintf(fs->f, "%4d %*lu %*llu %*llu %*lu\n", fs->ext,
85 fs->logical_width,
86 (unsigned long) fs->logical_start,
87 fs->physical_width, fs->physical_start,
88 fs->physical_width, fs->expected,
89 fs->logical_width, (unsigned long) fs->num);
90 else
91 fprintf(fs->f, "%4d %*lu %*llu %*s %*lu\n", fs->ext,
92 fs->logical_width,
93 (unsigned long) fs->logical_start,
94 fs->physical_width, fs->physical_start,
95 fs->physical_width, "",
96 fs->logical_width, (unsigned long) fs->num);
97 }
98 fs->ext++;
99 }
100
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)101 static int filefrag_blocks_proc(ext2_filsys ext4_fs EXT2FS_ATTR((unused)),
102 blk64_t *blocknr, e2_blkcnt_t blockcnt,
103 blk64_t ref_block EXT2FS_ATTR((unused)),
104 int ref_offset EXT2FS_ATTR((unused)),
105 void *private)
106 {
107 struct filefrag_struct *fs = private;
108
109 if (blockcnt < 0 || *blocknr == 0)
110 return 0;
111
112 if ((fs->num == 0) || (blockcnt != fs->logical_start + fs->num) ||
113 (*blocknr != fs->physical_start + fs->num)) {
114 report_filefrag(fs);
115 if (blockcnt == fs->logical_start + fs->num)
116 fs->expected = fs->physical_start + fs->num;
117 else
118 fs->expected = 0;
119 fs->logical_start = blockcnt;
120 fs->physical_start = *blocknr;
121 fs->num = 1;
122 fs->cont_ext++;
123 } else
124 fs->num++;
125 return 0;
126 }
127
filefrag(ext2_ino_t ino,struct ext2_inode * inode,struct filefrag_struct * fs)128 static void filefrag(ext2_ino_t ino, struct ext2_inode *inode,
129 struct filefrag_struct *fs)
130 {
131 errcode_t retval;
132 int blocksize = current_fs->blocksize;
133
134 fs->logical_width = int_log10((EXT2_I_SIZE(inode) + blocksize - 1) /
135 blocksize) + 1;
136 if (fs->logical_width < 7)
137 fs->logical_width = 7;
138 fs->ext = 0;
139 fs->cont_ext = 0;
140 fs->logical_start = 0;
141 fs->physical_start = 0;
142 fs->num = 0;
143
144 if (fs->options & VERBOSE_OPT) {
145 blk64_t num_blocks = ext2fs_inode_i_blocks(current_fs, inode);
146
147 if (!(current_fs->super->s_feature_ro_compat &
148 EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
149 !(inode->i_flags & EXT4_HUGE_FILE_FL))
150 num_blocks /= current_fs->blocksize / 512;
151
152 fprintf(fs->f, "\n%s has %llu block(s), i_size is %llu\n",
153 fs->name, num_blocks, EXT2_I_SIZE(inode));
154 }
155 print_header(fs);
156 retval = ext2fs_block_iterate3(current_fs, ino,
157 BLOCK_FLAG_READ_ONLY, NULL,
158 filefrag_blocks_proc, fs);
159 if (retval)
160 com_err("ext2fs_block_iterate3", retval, 0);
161
162 report_filefrag(fs);
163 fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
164 LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : "");
165 }
166
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)167 static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
168 int entry,
169 struct ext2_dir_entry *dirent,
170 int offset EXT2FS_ATTR((unused)),
171 int blocksize EXT2FS_ATTR((unused)),
172 char *buf EXT2FS_ATTR((unused)),
173 void *private)
174 {
175 struct filefrag_struct *fs = private;
176 struct ext2_inode inode;
177 ext2_ino_t ino;
178 char name[EXT2_NAME_LEN + 1];
179 char *cp;
180 int thislen;
181
182 if (entry == DIRENT_DELETED_FILE)
183 return 0;
184
185 thislen = dirent->name_len & 0xFF;
186 strncpy(name, dirent->name, thislen);
187 name[thislen] = '\0';
188 ino = dirent->inode;
189
190 if (!strcmp(name, ".") || !strcmp(name, ".."))
191 return 0;
192
193 cp = malloc(strlen(fs->dir_name) + strlen(name) + 2);
194 if (!cp) {
195 fprintf(stderr, "Couldn't allocate memory for %s/%s\n",
196 fs->dir_name, name);
197 return 0;
198 }
199
200 sprintf(cp, "%s/%s", fs->dir_name, name);
201 fs->name = cp;
202
203 if (debugfs_read_inode(ino, &inode, fs->name))
204 goto errout;
205
206 filefrag(ino, &inode, fs);
207
208 if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) {
209 struct dir_list *p;
210
211 p = malloc(sizeof(struct dir_list));
212 if (!p) {
213 fprintf(stderr, "Couldn't allocate dir_list for %s\n",
214 fs->name);
215 goto errout;
216 }
217 memset(p, 0, sizeof(struct dir_list));
218 p->name = cp;
219 p->ino = ino;
220 if (fs->dir_last)
221 fs->dir_last->next = p;
222 else
223 fs->dir_list = p;
224 fs->dir_last = p;
225 return 0;
226 }
227 errout:
228 free(cp);
229 fs->name = 0;
230 return 0;
231 }
232
233
dir_iterate(ext2_ino_t ino,struct filefrag_struct * fs)234 static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs)
235 {
236 errcode_t retval;
237 struct dir_list *p = NULL;
238
239 fs->dir_name = fs->name;
240
241 while (1) {
242 retval = ext2fs_dir_iterate2(current_fs, ino, 0,
243 0, filefrag_dir_proc, fs);
244 if (retval)
245 com_err("ext2fs_dir_iterate2", retval, 0);
246 if (p) {
247 free(p->name);
248 fs->dir_list = p->next;
249 if (!fs->dir_list)
250 fs->dir_last = 0;
251 free(p);
252 }
253 p = fs->dir_list;
254 if (!p)
255 break;
256 ino = p->ino;
257 fs->dir_name = p->name;
258 }
259 }
260
do_filefrag(int argc,char * argv[])261 void do_filefrag(int argc, char *argv[])
262 {
263 struct filefrag_struct fs;
264 struct ext2_inode inode;
265 ext2_ino_t ino;
266 int c;
267
268 memset(&fs, 0, sizeof(fs));
269 if (check_fs_open(argv[0]))
270 return;
271
272 reset_getopt();
273 while ((c = getopt(argc, argv, "dvr")) != EOF) {
274 switch (c) {
275 case 'd':
276 fs.options |= DIR_OPT;
277 break;
278 case 'v':
279 fs.options |= VERBOSE_OPT;
280 break;
281 case 'r':
282 fs.options |= RECURSIVE_OPT;
283 break;
284 default:
285 goto print_usage;
286 }
287 }
288
289 if (argc > optind+1) {
290 print_usage:
291 com_err(0, 0, "Usage: filefrag [-dvr] file");
292 return;
293 }
294
295 if (argc == optind) {
296 ino = cwd;
297 fs.name = ".";
298 } else {
299 ino = string_to_inode(argv[optind]);
300 fs.name = argv[optind];
301 }
302 if (!ino)
303 return;
304
305 if (debugfs_read_inode(ino, &inode, argv[0]))
306 return;
307
308 fs.f = open_pager();
309 fs.physical_width = int_log10(ext2fs_blocks_count(current_fs->super));
310 fs.physical_width++;
311 if (fs.physical_width < 8)
312 fs.physical_width = 8;
313
314 if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT))
315 filefrag(ino, &inode, &fs);
316 else
317 dir_iterate(ino, &fs);
318
319 fprintf(fs.f, "\n");
320 close_pager(fs.f);
321
322 return;
323 }
324