filefrag.c revision 5093775ab446d375ea753218c364be31cf82f3cc
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 25extern int optind; 26extern 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 35struct dir_list { 36 char *name; 37 ext2_ino_t ino; 38 struct dir_list *next; 39}; 40 41struct 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 57static 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 69static 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 79static 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 102static 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 129static 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 168static 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 } 228errout: 229 free(cp); 230 fs->name = 0; 231 return 0; 232} 233 234 235static 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 262void 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} 325