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