Home | History | Annotate | Download | only in debugfs
      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 (!ext2fs_has_feature_huge_file(current_fs->super) ||
    149 		    !(inode->i_flags & EXT4_HUGE_FILE_FL))
    150 			num_blocks /= current_fs->blocksize / 512;
    151 
    152 		fprintf(fs->f, "\n%s has %llu block(s), i_size is %llu\n",
    153 			fs->name, num_blocks, EXT2_I_SIZE(inode));
    154 	}
    155 	print_header(fs);
    156 	if (ext2fs_inode_has_valid_blocks2(current_fs, inode)) {
    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 
    164 	report_filefrag(fs);
    165 	fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
    166 		LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : "");
    167 }
    168 
    169 static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
    170 			     int	entry,
    171 			     struct ext2_dir_entry *dirent,
    172 			     int	offset EXT2FS_ATTR((unused)),
    173 			     int	blocksize EXT2FS_ATTR((unused)),
    174 			     char	*buf EXT2FS_ATTR((unused)),
    175 			     void	*private)
    176 {
    177 	struct filefrag_struct *fs = private;
    178 	struct ext2_inode	inode;
    179 	ext2_ino_t		ino;
    180 	char			name[EXT2_NAME_LEN + 1];
    181 	char			*cp;
    182 	int			thislen;
    183 
    184 	if (entry == DIRENT_DELETED_FILE)
    185 		return 0;
    186 
    187 	thislen = ext2fs_dirent_name_len(dirent);
    188 	strncpy(name, dirent->name, thislen);
    189 	name[thislen] = '\0';
    190 	ino = dirent->inode;
    191 
    192 	if (!strcmp(name, ".") || !strcmp(name, ".."))
    193 		return 0;
    194 
    195 	cp = malloc(strlen(fs->dir_name) + strlen(name) + 2);
    196 	if (!cp) {
    197 		fprintf(stderr, "Couldn't allocate memory for %s/%s\n",
    198 			fs->dir_name, name);
    199 		return 0;
    200 	}
    201 
    202 	sprintf(cp, "%s/%s", fs->dir_name, name);
    203 	fs->name = cp;
    204 
    205 	if (debugfs_read_inode(ino, &inode, fs->name))
    206 		goto errout;
    207 
    208 	filefrag(ino, &inode, fs);
    209 
    210 	if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) {
    211 		struct dir_list *p;
    212 
    213 		p = malloc(sizeof(struct dir_list));
    214 		if (!p) {
    215 			fprintf(stderr, "Couldn't allocate dir_list for %s\n",
    216 				fs->name);
    217 			goto errout;
    218 		}
    219 		memset(p, 0, sizeof(struct dir_list));
    220 		p->name = cp;
    221 		p->ino = ino;
    222 		if (fs->dir_last)
    223 			fs->dir_last->next = p;
    224 		else
    225 			fs->dir_list = p;
    226 		fs->dir_last = p;
    227 		return 0;
    228 	}
    229 errout:
    230 	free(cp);
    231 	fs->name = 0;
    232 	return 0;
    233 }
    234 
    235 
    236 static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs)
    237 {
    238 	errcode_t	retval;
    239 	struct dir_list	*p = NULL;
    240 
    241 	fs->dir_name = fs->name;
    242 
    243 	while (1) {
    244 		retval = ext2fs_dir_iterate2(current_fs, ino, 0,
    245 					     0, filefrag_dir_proc, fs);
    246 		if (retval)
    247 			com_err("ext2fs_dir_iterate2", retval, 0);
    248 		if (p) {
    249 			free(p->name);
    250 			fs->dir_list = p->next;
    251 			if (!fs->dir_list)
    252 				fs->dir_last = 0;
    253 			free(p);
    254 		}
    255 		p = fs->dir_list;
    256 		if (!p)
    257 			break;
    258 		ino = p->ino;
    259 		fs->dir_name = p->name;
    260 	}
    261 }
    262 
    263 void do_filefrag(int argc, char *argv[])
    264 {
    265 	struct filefrag_struct fs;
    266 	struct ext2_inode inode;
    267 	ext2_ino_t	ino;
    268 	int		c;
    269 
    270 	memset(&fs, 0, sizeof(fs));
    271 	if (check_fs_open(argv[0]))
    272 		return;
    273 
    274 	reset_getopt();
    275 	while ((c = getopt(argc, argv, "dvr")) != EOF) {
    276 		switch (c) {
    277 		case 'd':
    278 			fs.options |= DIR_OPT;
    279 			break;
    280 		case 'v':
    281 			fs.options |= VERBOSE_OPT;
    282 			break;
    283 		case 'r':
    284 			fs.options |= RECURSIVE_OPT;
    285 			break;
    286 		default:
    287 			goto print_usage;
    288 		}
    289 	}
    290 
    291 	if (argc > optind+1) {
    292 	print_usage:
    293 		com_err(0, 0, "Usage: filefrag [-dvr] file");
    294 		return;
    295 	}
    296 
    297 	if (argc == optind) {
    298 		ino = cwd;
    299 		fs.name = ".";
    300 	} else {
    301 		ino = string_to_inode(argv[optind]);
    302 		fs.name = argv[optind];
    303 	}
    304 	if (!ino)
    305 		return;
    306 
    307 	if (debugfs_read_inode(ino, &inode, argv[0]))
    308 		return;
    309 
    310 	fs.f = open_pager();
    311 	fs.physical_width = int_log10(ext2fs_blocks_count(current_fs->super));
    312 	fs.physical_width++;
    313 	if (fs.physical_width < 8)
    314 		fs.physical_width = 8;
    315 
    316 	if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT))
    317 		filefrag(ino, &inode, &fs);
    318 	else
    319 		dir_iterate(ino, &fs);
    320 
    321 	fprintf(fs.f, "\n");
    322 	close_pager(fs.f);
    323 
    324 	return;
    325 }
    326