Whamcloud - gitweb
LU-17117 misc: deduplicate log2/log10 functions
[tools/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 void print_header(struct filefrag_struct *fs)
58 {
59         if (fs->options & VERBOSE_OPT) {
60                 fprintf(fs->f, "%4s %*s %*s %*s %*s\n", "ext",
61                         fs->logical_width, "logical", fs->physical_width,
62                         "physical", fs->physical_width, "expected",
63                         fs->logical_width, "length");
64         }
65 }
66
67 static void report_filefrag(struct filefrag_struct *fs)
68 {
69         if (fs->num == 0)
70                 return;
71         if (fs->options & VERBOSE_OPT) {
72                 if (fs->expected)
73                         fprintf(fs->f, "%4d %*lu %*llu %*llu %*lu\n", fs->ext,
74                                 fs->logical_width,
75                                 (unsigned long) fs->logical_start,
76                                 fs->physical_width,
77                                 (unsigned long long) fs->physical_start,
78                                 fs->physical_width,
79                                 (unsigned long long) fs->expected,
80                                 fs->logical_width, (unsigned long) fs->num);
81                 else
82                         fprintf(fs->f, "%4d %*lu %*llu %*s %*lu\n", fs->ext,
83                                 fs->logical_width,
84                                 (unsigned long) fs->logical_start,
85                                 fs->physical_width,
86                                 (unsigned long long) fs->physical_start,
87                                 fs->physical_width, "",
88                                 fs->logical_width, (unsigned long) fs->num);
89         }
90         fs->ext++;
91 }
92
93 static int filefrag_blocks_proc(ext2_filsys ext4_fs EXT2FS_ATTR((unused)),
94                                 blk64_t *blocknr, e2_blkcnt_t blockcnt,
95                                 blk64_t ref_block EXT2FS_ATTR((unused)),
96                                 int ref_offset EXT2FS_ATTR((unused)),
97                                 void *private)
98 {
99         struct filefrag_struct *fs = private;
100
101         if (blockcnt < 0 || *blocknr == 0)
102                 return 0;
103
104         if ((fs->num == 0) || (blockcnt != fs->logical_start + fs->num) ||
105             (*blocknr != fs->physical_start + fs->num)) {
106                 report_filefrag(fs);
107                 if (blockcnt == fs->logical_start + fs->num)
108                         fs->expected = fs->physical_start + fs->num;
109                 else
110                         fs->expected = 0;
111                 fs->logical_start = blockcnt;
112                 fs->physical_start = *blocknr;
113                 fs->num = 1;
114                 fs->cont_ext++;
115         } else
116                 fs->num++;
117         return 0;
118 }
119
120 static void filefrag(ext2_ino_t ino, struct ext2_inode *inode,
121                      struct filefrag_struct *fs)
122 {
123         errcode_t       retval;
124         int             blocksize = current_fs->blocksize;
125
126         fs->logical_width = ext2fs_log10((EXT2_I_SIZE(inode) + blocksize - 1) /
127                                          blocksize) + 1;
128         if (fs->logical_width < 7)
129                 fs->logical_width = 7;
130         fs->ext = 0;
131         fs->cont_ext = 0;
132         fs->logical_start = 0;
133         fs->physical_start = 0;
134         fs->num = 0;
135
136         if (fs->options & VERBOSE_OPT) {
137                 blk64_t num_blocks = ext2fs_inode_i_blocks(current_fs, inode);
138
139                 if (!ext2fs_has_feature_huge_file(current_fs->super) ||
140                     !(inode->i_flags & EXT4_HUGE_FILE_FL))
141                         num_blocks /= current_fs->blocksize / 512;
142
143                 fprintf(fs->f, "\n%s has %llu block(s), i_size is %llu\n",
144                         fs->name, (unsigned long long) num_blocks,
145                         (unsigned long long) EXT2_I_SIZE(inode));
146         }
147         print_header(fs);
148         if (ext2fs_inode_has_valid_blocks2(current_fs, inode)) {
149                 retval = ext2fs_block_iterate3(current_fs, ino,
150                                                BLOCK_FLAG_READ_ONLY, NULL,
151                                                filefrag_blocks_proc, fs);
152                 if (retval)
153                         com_err("ext2fs_block_iterate3", retval, 0);
154         }
155
156         report_filefrag(fs);
157         fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
158                 LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : "");
159 }
160
161 static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
162                              int        entry,
163                              struct ext2_dir_entry *dirent,
164                              int        offset EXT2FS_ATTR((unused)),
165                              int        blocksize EXT2FS_ATTR((unused)),
166                              char       *buf EXT2FS_ATTR((unused)),
167                              void       *private)
168 {
169         struct filefrag_struct *fs = private;
170         struct ext2_inode       inode;
171         ext2_ino_t              ino;
172         char                    name[EXT2_NAME_LEN + 1];
173         char                    *cp;
174         int                     thislen;
175
176         if (entry == DIRENT_DELETED_FILE)
177                 return 0;
178
179         thislen = ext2fs_dirent_name_len(dirent);
180         strncpy(name, dirent->name, thislen);
181         name[thislen] = '\0';
182         ino = dirent->inode;
183
184         if (!strcmp(name, ".") || !strcmp(name, ".."))
185                 return 0;
186
187         cp = malloc(strlen(fs->dir_name) + strlen(name) + 2);
188         if (!cp) {
189                 fprintf(stderr, "Couldn't allocate memory for %s/%s\n",
190                         fs->dir_name, name);
191                 return 0;
192         }
193
194         sprintf(cp, "%s/%s", fs->dir_name, name);
195         fs->name = cp;
196
197         if (debugfs_read_inode(ino, &inode, fs->name))
198                 goto errout;
199
200         filefrag(ino, &inode, fs);
201
202         if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) {
203                 struct dir_list *p;
204
205                 p = malloc(sizeof(struct dir_list));
206                 if (!p) {
207                         fprintf(stderr, "Couldn't allocate dir_list for %s\n",
208                                 fs->name);
209                         goto errout;
210                 }
211                 memset(p, 0, sizeof(struct dir_list));
212                 p->name = cp;
213                 p->ino = ino;
214                 if (fs->dir_last)
215                         fs->dir_last->next = p;
216                 else
217                         fs->dir_list = p;
218                 fs->dir_last = p;
219                 return 0;
220         }
221 errout:
222         free(cp);
223         fs->name = 0;
224         return 0;
225 }
226
227
228 static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs)
229 {
230         errcode_t       retval;
231         struct dir_list *p = NULL;
232
233         fs->dir_name = fs->name;
234
235         while (1) {
236                 retval = ext2fs_dir_iterate2(current_fs, ino, 0,
237                                              0, filefrag_dir_proc, fs);
238                 if (retval)
239                         com_err("ext2fs_dir_iterate2", retval, 0);
240                 if (p) {
241                         free(p->name);
242                         fs->dir_list = p->next;
243                         if (!fs->dir_list)
244                                 fs->dir_last = 0;
245                         free(p);
246                 }
247                 p = fs->dir_list;
248                 if (!p)
249                         break;
250                 ino = p->ino;
251                 fs->dir_name = p->name;
252         }
253 }
254
255 void do_filefrag(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
256                  void *infop EXT2FS_ATTR((unused)))
257 {
258         struct filefrag_struct fs;
259         struct ext2_inode inode;
260         ext2_ino_t      ino;
261         int             c;
262
263         memset(&fs, 0, sizeof(fs));
264         if (check_fs_open(argv[0]))
265                 return;
266
267         reset_getopt();
268         while ((c = getopt(argc, argv, "dvr")) != EOF) {
269                 switch (c) {
270                 case 'd':
271                         fs.options |= DIR_OPT;
272                         break;
273                 case 'v':
274                         fs.options |= VERBOSE_OPT;
275                         break;
276                 case 'r':
277                         fs.options |= RECURSIVE_OPT;
278                         break;
279                 default:
280                         goto print_usage;
281                 }
282         }
283
284         if (argc > optind+1) {
285         print_usage:
286                 com_err(0, 0, "Usage: filefrag [-dvr] file");
287                 return;
288         }
289
290         if (argc == optind) {
291                 ino = cwd;
292                 fs.name = ".";
293         } else {
294                 ino = string_to_inode(argv[optind]);
295                 fs.name = argv[optind];
296         }
297         if (!ino)
298                 return;
299
300         if (debugfs_read_inode(ino, &inode, argv[0]))
301                 return;
302
303         fs.f = open_pager();
304         fs.physical_width = ext2fs_log10(ext2fs_blocks_count(current_fs->super));
305         fs.physical_width++;
306         if (fs.physical_width < 8)
307                 fs.physical_width = 8;
308
309         if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT))
310                 filefrag(ino, &inode, &fs);
311         else
312                 dir_iterate(ino, &fs);
313
314         fprintf(fs.f, "\n");
315         close_pager(fs.f);
316
317         return;
318 }