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