• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
int_log10(unsigned long long arg)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 
print_header(struct filefrag_struct * fs)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 
report_filefrag(struct filefrag_struct * fs)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 
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)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 
filefrag(ext2_ino_t ino,struct ext2_inode * inode,struct filefrag_struct * fs)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 
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)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 
dir_iterate(ext2_ino_t ino,struct filefrag_struct * fs)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 
do_filefrag(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))267 void do_filefrag(int argc, char *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 }
331