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