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 <stdio.h>
      9 #include <unistd.h>
     10 #include <stdlib.h>
     11 #include <ctype.h>
     12 #include <string.h>
     13 #include <time.h>
     14 #ifdef HAVE_ERRNO_H
     15 #include <errno.h>
     16 #endif
     17 #include <sys/types.h>
     18 #include <sys/stat.h>
     19 #include <fcntl.h>
     20 #include <utime.h>
     21 #ifdef HAVE_GETOPT_H
     22 #include <getopt.h>
     23 #else
     24 extern int optind;
     25 extern char *optarg;
     26 #endif
     27 
     28 #include "debugfs.h"
     29 
     30 #define VERBOSE_OPT	0x0001
     31 #define DIR_OPT		0x0002
     32 #define RECURSIVE_OPT	0x0004
     33 
     34 struct dir_list {
     35 	char		*name;
     36 	ext2_ino_t	ino;
     37 	struct dir_list	*next;
     38 };
     39 
     40 struct filefrag_struct {
     41 	FILE		*f;
     42 	const char	*name;
     43 	const char	*dir_name;
     44 	int		options;
     45 	int		logical_width;
     46 	int		physical_width;
     47 	int		ext;
     48 	int		cont_ext;
     49 	e2_blkcnt_t	num;
     50 	e2_blkcnt_t	logical_start;
     51 	blk64_t		physical_start;
     52 	blk64_t		expected;
     53 	struct dir_list *dir_list, *dir_last;
     54 };
     55 
     56 static int int_log10(unsigned long long arg)
     57 {
     58 	int     l = 0;
     59 
     60 	arg = arg / 10;
     61 	while (arg) {
     62 		l++;
     63 		arg = arg / 10;
     64 	}
     65 	return l;
     66 }
     67 
     68 static void print_header(struct filefrag_struct *fs)
     69 {
     70 	if (fs->options & VERBOSE_OPT) {
     71 		fprintf(fs->f, "%4s %*s %*s %*s %*s\n", "ext",
     72 			fs->logical_width, "logical", fs->physical_width,
     73 			"physical", fs->physical_width, "expected",
     74 			fs->logical_width, "length");
     75 	}
     76 }
     77 
     78 static void report_filefrag(struct filefrag_struct *fs)
     79 {
     80 	if (fs->num == 0)
     81 		return;
     82 	if (fs->options & VERBOSE_OPT) {
     83 		if (fs->expected)
     84 			fprintf(fs->f, "%4d %*lu %*llu %*llu %*lu\n", fs->ext,
     85 				fs->logical_width,
     86 				(unsigned long) fs->logical_start,
     87 				fs->physical_width, fs->physical_start,
     88 				fs->physical_width, fs->expected,
     89 				fs->logical_width, (unsigned long) fs->num);
     90 		else
     91 			fprintf(fs->f, "%4d %*lu %*llu %*s %*lu\n", fs->ext,
     92 				fs->logical_width,
     93 				(unsigned long) fs->logical_start,
     94 				fs->physical_width, fs->physical_start,
     95 				fs->physical_width, "",
     96 				fs->logical_width, (unsigned long) fs->num);
     97 	}
     98 	fs->ext++;
     99 }
    100 
    101 static int filefrag_blocks_proc(ext2_filsys ext4_fs EXT2FS_ATTR((unused)),
    102 				blk64_t *blocknr, e2_blkcnt_t blockcnt,
    103 				blk64_t ref_block EXT2FS_ATTR((unused)),
    104 				int ref_offset EXT2FS_ATTR((unused)),
    105 				void *private)
    106 {
    107 	struct filefrag_struct *fs = private;
    108 
    109 	if (blockcnt < 0 || *blocknr == 0)
    110 		return 0;
    111 
    112 	if ((fs->num == 0) || (blockcnt != fs->logical_start + fs->num) ||
    113 	    (*blocknr != fs->physical_start + fs->num)) {
    114 		report_filefrag(fs);
    115 		if (blockcnt == fs->logical_start + fs->num)
    116 			fs->expected = fs->physical_start + fs->num;
    117 		else
    118 			fs->expected = 0;
    119 		fs->logical_start = blockcnt;
    120 		fs->physical_start = *blocknr;
    121 		fs->num = 1;
    122 		fs->cont_ext++;
    123 	} else
    124 		fs->num++;
    125 	return 0;
    126 }
    127 
    128 static void filefrag(ext2_ino_t ino, struct ext2_inode *inode,
    129 		     struct filefrag_struct *fs)
    130 {
    131 	errcode_t	retval;
    132 	int		blocksize = current_fs->blocksize;
    133 
    134 	fs->logical_width = int_log10((EXT2_I_SIZE(inode) + blocksize - 1) /
    135 				      blocksize) + 1;
    136 	if (fs->logical_width < 7)
    137 		fs->logical_width = 7;
    138 	fs->ext = 0;
    139 	fs->cont_ext = 0;
    140 	fs->logical_start = 0;
    141 	fs->physical_start = 0;
    142 	fs->num = 0;
    143 
    144 	if (fs->options & VERBOSE_OPT) {
    145 		blk64_t num_blocks = ext2fs_inode_i_blocks(current_fs, inode);
    146 
    147 		if (!(current_fs->super->s_feature_ro_compat &
    148 		     EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
    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 	retval = ext2fs_block_iterate3(current_fs, ino,
    157 				       BLOCK_FLAG_READ_ONLY, NULL,
    158 				       filefrag_blocks_proc, fs);
    159 	if (retval)
    160 		com_err("ext2fs_block_iterate3", retval, 0);
    161 
    162 	report_filefrag(fs);
    163 	fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
    164 		LINUX_S_ISDIR(inode->i_mode) ? " (dir)" : "");
    165 }
    166 
    167 static int filefrag_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
    168 			     int	entry,
    169 			     struct ext2_dir_entry *dirent,
    170 			     int	offset EXT2FS_ATTR((unused)),
    171 			     int	blocksize EXT2FS_ATTR((unused)),
    172 			     char	*buf EXT2FS_ATTR((unused)),
    173 			     void	*private)
    174 {
    175 	struct filefrag_struct *fs = private;
    176 	struct ext2_inode	inode;
    177 	ext2_ino_t		ino;
    178 	char			name[EXT2_NAME_LEN + 1];
    179 	char			*cp;
    180 	int			thislen;
    181 
    182 	if (entry == DIRENT_DELETED_FILE)
    183 		return 0;
    184 
    185 	thislen = dirent->name_len & 0xFF;
    186 	strncpy(name, dirent->name, thislen);
    187 	name[thislen] = '\0';
    188 	ino = dirent->inode;
    189 
    190 	if (!strcmp(name, ".") || !strcmp(name, ".."))
    191 		return 0;
    192 
    193 	cp = malloc(strlen(fs->dir_name) + strlen(name) + 2);
    194 	if (!cp) {
    195 		fprintf(stderr, "Couldn't allocate memory for %s/%s\n",
    196 			fs->dir_name, name);
    197 		return 0;
    198 	}
    199 
    200 	sprintf(cp, "%s/%s", fs->dir_name, name);
    201 	fs->name = cp;
    202 
    203 	if (debugfs_read_inode(ino, &inode, fs->name))
    204 		goto errout;
    205 
    206 	filefrag(ino, &inode, fs);
    207 
    208 	if ((fs->options & RECURSIVE_OPT) && LINUX_S_ISDIR(inode.i_mode)) {
    209 		struct dir_list *p;
    210 
    211 		p = malloc(sizeof(struct dir_list));
    212 		if (!p) {
    213 			fprintf(stderr, "Couldn't allocate dir_list for %s\n",
    214 				fs->name);
    215 			goto errout;
    216 		}
    217 		memset(p, 0, sizeof(struct dir_list));
    218 		p->name = cp;
    219 		p->ino = ino;
    220 		if (fs->dir_last)
    221 			fs->dir_last->next = p;
    222 		else
    223 			fs->dir_list = p;
    224 		fs->dir_last = p;
    225 		return 0;
    226 	}
    227 errout:
    228 	free(cp);
    229 	fs->name = 0;
    230 	return 0;
    231 }
    232 
    233 
    234 static void dir_iterate(ext2_ino_t ino, struct filefrag_struct *fs)
    235 {
    236 	errcode_t	retval;
    237 	struct dir_list	*p = NULL;
    238 
    239 	fs->dir_name = fs->name;
    240 
    241 	while (1) {
    242 		retval = ext2fs_dir_iterate2(current_fs, ino, 0,
    243 					     0, filefrag_dir_proc, fs);
    244 		if (retval)
    245 			com_err("ext2fs_dir_iterate2", retval, 0);
    246 		if (p) {
    247 			free(p->name);
    248 			fs->dir_list = p->next;
    249 			if (!fs->dir_list)
    250 				fs->dir_last = 0;
    251 			free(p);
    252 		}
    253 		p = fs->dir_list;
    254 		if (!p)
    255 			break;
    256 		ino = p->ino;
    257 		fs->dir_name = p->name;
    258 	}
    259 }
    260 
    261 void do_filefrag(int argc, char *argv[])
    262 {
    263 	struct filefrag_struct fs;
    264 	struct ext2_inode inode;
    265 	ext2_ino_t	ino;
    266 	int		c;
    267 
    268 	memset(&fs, 0, sizeof(fs));
    269 	if (check_fs_open(argv[0]))
    270 		return;
    271 
    272 	reset_getopt();
    273 	while ((c = getopt(argc, argv, "dvr")) != EOF) {
    274 		switch (c) {
    275 		case 'd':
    276 			fs.options |= DIR_OPT;
    277 			break;
    278 		case 'v':
    279 			fs.options |= VERBOSE_OPT;
    280 			break;
    281 		case 'r':
    282 			fs.options |= RECURSIVE_OPT;
    283 			break;
    284 		default:
    285 			goto print_usage;
    286 		}
    287 	}
    288 
    289 	if (argc > optind+1) {
    290 	print_usage:
    291 		com_err(0, 0, "Usage: filefrag [-dvr] file");
    292 		return;
    293 	}
    294 
    295 	if (argc == optind) {
    296 		ino = cwd;
    297 		fs.name = ".";
    298 	} else {
    299 		ino = string_to_inode(argv[optind]);
    300 		fs.name = argv[optind];
    301 	}
    302 	if (!ino)
    303 		return;
    304 
    305 	if (debugfs_read_inode(ino, &inode, argv[0]))
    306 		return;
    307 
    308 	fs.f = open_pager();
    309 	fs.physical_width = int_log10(ext2fs_blocks_count(current_fs->super));
    310 	fs.physical_width++;
    311 	if (fs.physical_width < 8)
    312 		fs.physical_width = 8;
    313 
    314 	if (!LINUX_S_ISDIR(inode.i_mode) || (fs.options & DIR_OPT))
    315 		filefrag(ino, &inode, &fs);
    316 	else
    317 		dir_iterate(ino, &fs);
    318 
    319 	fprintf(fs.f, "\n");
    320 	close_pager(fs.f);
    321 
    322 	return;
    323 }
    324