Whamcloud - gitweb
debugfs: update usage message for open and filefrag
[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 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, 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
102 static 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
129 static 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
168 static 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         }
228 errout:
229         free(cp);
230         fs->name = 0;
231         return 0;
232 }
233
234
235 static 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
262 void 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();
274         while ((c = getopt(argc, argv, "dvr")) != EOF) {
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 [-dvr] 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 }