]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - debugfs/filefrag.c
libext2fs: fix potential divide by zero bug caused by a lxcfs bug
[thirdparty/e2fsprogs.git] / debugfs / filefrag.c
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
25 extern int optind;
26 extern 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
35 struct dir_list {
36 char *name;
37 ext2_ino_t ino;
38 struct dir_list *next;
39 };
40
41 struct 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
57 static 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
69 static 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
79 static 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,
89 (unsigned long long) fs->physical_start,
90 fs->physical_width,
91 (unsigned long long) fs->expected,
92 fs->logical_width, (unsigned long) fs->num);
93 else
94 fprintf(fs->f, "%4d %*lu %*llu %*s %*lu\n", fs->ext,
95 fs->logical_width,
96 (unsigned long) fs->logical_start,
97 fs->physical_width,
98 (unsigned long long) fs->physical_start,
99 fs->physical_width, "",
100 fs->logical_width, (unsigned long) fs->num);
101 }
102 fs->ext++;
103 }
104
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)),
109 void *private)
110 {
111 struct filefrag_struct *fs = private;
112
113 if (blockcnt < 0 || *blocknr == 0)
114 return 0;
115
116 if ((fs->num == 0) || (blockcnt != fs->logical_start + fs->num) ||
117 (*blocknr != fs->physical_start + fs->num)) {
118 report_filefrag(fs);
119 if (blockcnt == fs->logical_start + fs->num)
120 fs->expected = fs->physical_start + fs->num;
121 else
122 fs->expected = 0;
123 fs->logical_start = blockcnt;
124 fs->physical_start = *blocknr;
125 fs->num = 1;
126 fs->cont_ext++;
127 } else
128 fs->num++;
129 return 0;
130 }
131
132 static void filefrag(ext2_ino_t ino, struct ext2_inode *inode,
133 struct filefrag_struct *fs)
134 {
135 errcode_t retval;
136 int blocksize = current_fs->blocksize;
137
138 fs->logical_width = int_log10((EXT2_I_SIZE(inode) + blocksize - 1) /
139 blocksize) + 1;
140 if (fs->logical_width < 7)
141 fs->logical_width = 7;
142 fs->ext = 0;
143 fs->cont_ext = 0;
144 fs->logical_start = 0;
145 fs->physical_start = 0;
146 fs->num = 0;
147
148 if (fs->options & VERBOSE_OPT) {
149 blk64_t num_blocks = ext2fs_inode_i_blocks(current_fs, inode);
150
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;
154
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));
158 }
159 print_header(fs);
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);
164 if (retval)
165 com_err("ext2fs_block_iterate3", retval, 0);
166 }
167
168 report_filefrag(fs);
169 fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
170 LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : "");
171 }
172
173 static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
174 int entry,
175 struct ext2_dir_entry *dirent,
176 int offset EXT2FS_ATTR((unused)),
177 int blocksize EXT2FS_ATTR((unused)),
178 char *buf EXT2FS_ATTR((unused)),
179 void *private)
180 {
181 struct filefrag_struct *fs = private;
182 struct ext2_inode inode;
183 ext2_ino_t ino;
184 char name[EXT2_NAME_LEN + 1];
185 char *cp;
186 int thislen;
187
188 if (entry == DIRENT_DELETED_FILE)
189 return 0;
190
191 thislen = ext2fs_dirent_name_len(dirent);
192 strncpy(name, dirent->name, thislen);
193 name[thislen] = '\0';
194 ino = dirent->inode;
195
196 if (!strcmp(name, ".") || !strcmp(name, ".."))
197 return 0;
198
199 cp = malloc(strlen(fs->dir_name) + strlen(name) + 2);
200 if (!cp) {
201 fprintf(stderr, "Couldn't allocate memory for %s/%s\n",
202 fs->dir_name, name);
203 return 0;
204 }
205
206 sprintf(cp, "%s/%s", fs->dir_name, name);
207 fs->name = cp;
208
209 if (debugfs_read_inode(ino, &inode, fs->name))
210 goto errout;
211
212 filefrag(ino, &inode, fs);
213
214 if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) {
215 struct dir_list *p;
216
217 p = malloc(sizeof(struct dir_list));
218 if (!p) {
219 fprintf(stderr, "Couldn't allocate dir_list for %s\n",
220 fs->name);
221 goto errout;
222 }
223 memset(p, 0, sizeof(struct dir_list));
224 p->name = cp;
225 p->ino = ino;
226 if (fs->dir_last)
227 fs->dir_last->next = p;
228 else
229 fs->dir_list = p;
230 fs->dir_last = p;
231 return 0;
232 }
233 errout:
234 free(cp);
235 fs->name = 0;
236 return 0;
237 }
238
239
240 static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs)
241 {
242 errcode_t retval;
243 struct dir_list *p = NULL;
244
245 fs->dir_name = fs->name;
246
247 while (1) {
248 retval = ext2fs_dir_iterate2(current_fs, ino, 0,
249 0, filefrag_dir_proc, fs);
250 if (retval)
251 com_err("ext2fs_dir_iterate2", retval, 0);
252 if (p) {
253 free(p->name);
254 fs->dir_list = p->next;
255 if (!fs->dir_list)
256 fs->dir_last = 0;
257 free(p);
258 }
259 p = fs->dir_list;
260 if (!p)
261 break;
262 ino = p->ino;
263 fs->dir_name = p->name;
264 }
265 }
266
267 void do_filefrag(int argc, ss_argv_t argv, int sci_idx EXT2FS_ATTR((unused)),
268 void *infop EXT2FS_ATTR((unused)))
269 {
270 struct filefrag_struct fs;
271 struct ext2_inode inode;
272 ext2_ino_t ino;
273 int c;
274
275 memset(&fs, 0, sizeof(fs));
276 if (check_fs_open(argv[0]))
277 return;
278
279 reset_getopt();
280 while ((c = getopt(argc, argv, "dvr")) != EOF) {
281 switch (c) {
282 case 'd':
283 fs.options |= DIR_OPT;
284 break;
285 case 'v':
286 fs.options |= VERBOSE_OPT;
287 break;
288 case 'r':
289 fs.options |= RECURSIVE_OPT;
290 break;
291 default:
292 goto print_usage;
293 }
294 }
295
296 if (argc > optind+1) {
297 print_usage:
298 com_err(0, 0, "Usage: filefrag [-dvr] file");
299 return;
300 }
301
302 if (argc == optind) {
303 ino = cwd;
304 fs.name = ".";
305 } else {
306 ino = string_to_inode(argv[optind]);
307 fs.name = argv[optind];
308 }
309 if (!ino)
310 return;
311
312 if (debugfs_read_inode(ino, &inode, argv[0]))
313 return;
314
315 fs.f = open_pager();
316 fs.physical_width = int_log10(ext2fs_blocks_count(current_fs->super));
317 fs.physical_width++;
318 if (fs.physical_width < 8)
319 fs.physical_width = 8;
320
321 if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT))
322 filefrag(ino, &inode, &fs);
323 else
324 dir_iterate(ino, &fs);
325
326 fprintf(fs.f, "\n");
327 close_pager(fs.f);
328
329 return;
330 }