1 /* 2 * e2freefrag - report filesystem free-space fragmentation 3 * 4 * Copyright (C) 2009 Sun Microsystems, Inc. 5 * 6 * Author: Rupesh Thakare <rupesh (at) sun.com> 7 * Andreas Dilger <adilger (at) sun.com> 8 * 9 * %Begin-Header% 10 * This file may be redistributed under the terms of the GNU Public 11 * License version 2. 12 * %End-Header% 13 */ 14 #include <stdio.h> 15 #ifdef HAVE_UNISTD_H 16 #include <unistd.h> 17 #endif 18 #ifdef HAVE_STDLIB_H 19 #include <stdlib.h> 20 #endif 21 #ifdef HAVE_GETOPT_H 22 #include <getopt.h> 23 #else 24 extern char *optarg; 25 extern int optind; 26 #endif 27 28 #include "ext2fs/ext2_fs.h" 29 #include "ext2fs/ext2fs.h" 30 #include "e2freefrag.h" 31 32 void usage(const char *prog) 33 { 34 fprintf(stderr, "usage: %s [-c chunksize in kb] [-h] " 35 "device_name\n", prog); 36 exit(1); 37 } 38 39 static int ul_log2(unsigned long arg) 40 { 41 int l = 0; 42 43 arg >>= 1; 44 while (arg) { 45 l++; 46 arg >>= 1; 47 } 48 return l; 49 } 50 51 void init_chunk_info(ext2_filsys fs, struct chunk_info *info) 52 { 53 int i; 54 55 info->blocksize_bits = ul_log2((unsigned long)fs->blocksize); 56 if (info->chunkbytes) { 57 info->chunkbits = ul_log2(info->chunkbytes); 58 info->blks_in_chunk = info->chunkbytes >> info->blocksize_bits; 59 } else { 60 info->chunkbits = ul_log2(DEFAULT_CHUNKSIZE); 61 info->blks_in_chunk = DEFAULT_CHUNKSIZE >> info->blocksize_bits; 62 } 63 64 info->min = ~0UL; 65 info->max = info->avg = 0; 66 info->real_free_chunks = 0; 67 68 for (i = 0; i < MAX_HIST; i++) { 69 info->histogram.fc_chunks[i] = 0; 70 info->histogram.fc_blocks[i] = 0; 71 } 72 } 73 74 void update_chunk_stats(struct chunk_info *info, unsigned long chunk_size) 75 { 76 unsigned long index; 77 78 index = ul_log2(chunk_size) + 1; 79 if (index >= MAX_HIST) 80 index = MAX_HIST-1; 81 info->histogram.fc_chunks[index]++; 82 info->histogram.fc_blocks[index] += chunk_size; 83 84 if (chunk_size > info->max) 85 info->max = chunk_size; 86 if (chunk_size < info->min) 87 info->min = chunk_size; 88 info->avg += chunk_size; 89 info->real_free_chunks++; 90 } 91 92 void scan_block_bitmap(ext2_filsys fs, struct chunk_info *info) 93 { 94 unsigned long long blocks_count = fs->super->s_blocks_count; 95 unsigned long long chunks = (blocks_count + info->blks_in_chunk) >> 96 (info->chunkbits - info->blocksize_bits); 97 unsigned long long chunk_num; 98 unsigned long last_chunk_size = 0; 99 unsigned long long chunk_start_blk = 0; 100 int used; 101 102 for (chunk_num = 0; chunk_num < chunks; chunk_num++) { 103 unsigned long long blk, num_blks; 104 int chunk_free; 105 106 /* Last chunk may be smaller */ 107 if (chunk_start_blk + info->blks_in_chunk > blocks_count) 108 num_blks = blocks_count - chunk_start_blk; 109 else 110 num_blks = info->blks_in_chunk; 111 112 chunk_free = 0; 113 114 /* Initialize starting block for first chunk correctly else 115 * there is a segfault when blocksize = 1024 in which case 116 * block_map->start = 1 */ 117 for (blk = 0; blk < num_blks; blk++, chunk_start_blk++) { 118 if (chunk_num == 0 && blk == 0) { 119 blk = fs->super->s_first_data_block; 120 chunk_start_blk = blk; 121 } 122 used = ext2fs_fast_test_block_bitmap(fs->block_map, 123 chunk_start_blk); 124 if (!used) { 125 last_chunk_size++; 126 chunk_free++; 127 } 128 129 if (used && last_chunk_size != 0) { 130 update_chunk_stats(info, last_chunk_size); 131 last_chunk_size = 0; 132 } 133 } 134 135 if (chunk_free == info->blks_in_chunk) 136 info->free_chunks++; 137 } 138 if (last_chunk_size != 0) 139 update_chunk_stats(info, last_chunk_size); 140 } 141 142 errcode_t get_chunk_info(ext2_filsys fs, struct chunk_info *info) 143 { 144 unsigned long total_chunks; 145 char *unitp = "KMGTPEZY"; 146 int units = 10; 147 unsigned long start = 0, end, cum; 148 int i, retval = 0; 149 150 scan_block_bitmap(fs, info); 151 152 printf("Total blocks: %u\nFree blocks: %u (%0.1f%%)\n", 153 fs->super->s_blocks_count, fs->super->s_free_blocks_count, 154 (double)fs->super->s_free_blocks_count * 100 / 155 fs->super->s_blocks_count); 156 157 if (info->chunkbytes) { 158 printf("\nChunksize: %lu bytes (%u blocks)\n", 159 info->chunkbytes, info->blks_in_chunk); 160 total_chunks = (fs->super->s_blocks_count + 161 info->blks_in_chunk) >> 162 (info->chunkbits - info->blocksize_bits); 163 printf("Total chunks: %lu\nFree chunks: %lu (%0.1f%%)\n", 164 total_chunks, info->free_chunks, 165 (double)info->free_chunks * 100 / total_chunks); 166 } 167 168 /* Display chunk information in KB */ 169 if (info->real_free_chunks) { 170 info->min = (info->min * fs->blocksize) >> 10; 171 info->max = (info->max * fs->blocksize) >> 10; 172 info->avg = (info->avg / info->real_free_chunks * 173 fs->blocksize) >> 10; 174 } else { 175 info->min = 0; 176 } 177 178 printf("\nMin. free extent: %lu KB \nMax. free extent: %lu KB\n" 179 "Avg. free extent: %lu KB\n", info->min, info->max, info->avg); 180 181 printf("\nHISTOGRAM OF FREE EXTENT SIZES:\n"); 182 printf("%s : %12s %12s %7s\n", "Extent Size Range", "Free extents", 183 "Free Blocks", "Percent"); 184 for (i = 0; i < MAX_HIST; i++) { 185 end = 1 << (i + info->blocksize_bits - units); 186 if (info->histogram.fc_chunks[i] != 0) { 187 char end_str[32]; 188 189 sprintf(end_str, "%5lu%c-", end, *unitp); 190 if (i == MAX_HIST-1) 191 strcpy(end_str, "max "); 192 printf("%5lu%c...%7s : %12lu %12lu %6.2f%%\n", 193 start, *unitp, end_str, 194 info->histogram.fc_chunks[i], 195 info->histogram.fc_blocks[i], 196 (double)info->histogram.fc_blocks[i] * 100 / 197 fs->super->s_free_blocks_count); 198 } 199 start = end; 200 if (start == 1<<10) { 201 start = 1; 202 units += 10; 203 unitp++; 204 } 205 } 206 207 return retval; 208 } 209 210 void close_device(char *device_name, ext2_filsys fs) 211 { 212 int retval = ext2fs_close(fs); 213 214 if (retval) 215 com_err(device_name, retval, "while closing the filesystem.\n"); 216 } 217 218 void collect_info(ext2_filsys fs, struct chunk_info *chunk_info) 219 { 220 unsigned int retval = 0, i, free_blks; 221 222 printf("Device: %s\n", fs->device_name); 223 printf("Blocksize: %u bytes\n", fs->blocksize); 224 225 retval = ext2fs_read_block_bitmap(fs); 226 if (retval) { 227 com_err(fs->device_name, retval, "while reading block bitmap"); 228 close_device(fs->device_name, fs); 229 exit(1); 230 } 231 232 init_chunk_info(fs, chunk_info); 233 234 retval = get_chunk_info(fs, chunk_info); 235 if (retval) { 236 com_err(fs->device_name, retval, "while collecting chunk info"); 237 close_device(fs->device_name, fs); 238 exit(1); 239 } 240 } 241 242 void open_device(char *device_name, ext2_filsys *fs) 243 { 244 int retval; 245 int flag = EXT2_FLAG_FORCE; 246 247 retval = ext2fs_open(device_name, flag, 0, 0, unix_io_manager, fs); 248 if (retval) { 249 com_err(device_name, retval, "while opening filesystem"); 250 exit(1); 251 } 252 } 253 254 int main(int argc, char *argv[]) 255 { 256 struct chunk_info chunk_info = { }; 257 errcode_t retval = 0; 258 ext2_filsys fs = NULL; 259 char *device_name; 260 char *progname; 261 char c, *end; 262 263 add_error_table(&et_ext2_error_table); 264 progname = argv[0]; 265 266 while ((c = getopt(argc, argv, "c:h")) != EOF) { 267 switch (c) { 268 case 'c': 269 chunk_info.chunkbytes = strtoull(optarg, &end, 0); 270 if (*end != '\0') { 271 fprintf(stderr, "%s: bad chunk size '%s'\n", 272 progname, optarg); 273 usage(progname); 274 } 275 if (chunk_info.chunkbytes & 276 (chunk_info.chunkbytes - 1)) { 277 fprintf(stderr, "%s: chunk size must be a " 278 "power of 2.\n", argv[0]); 279 usage(progname); 280 } 281 chunk_info.chunkbytes *= 1024; 282 break; 283 default: 284 fprintf(stderr, "%s: bad option '%c'\n", 285 progname, c); 286 case 'h': 287 usage(progname); 288 break; 289 } 290 } 291 292 if (optind == argc) { 293 fprintf(stderr, "%s: missing device name.\n", progname); 294 usage(progname); 295 } 296 297 device_name = argv[optind]; 298 299 open_device(device_name, &fs); 300 301 if (chunk_info.chunkbytes && (chunk_info.chunkbytes < fs->blocksize)) { 302 fprintf(stderr, "%s: chunksize must be greater than or equal " 303 "to filesystem blocksize.\n", progname); 304 exit(1); 305 } 306 collect_info(fs, &chunk_info); 307 close_device(device_name, fs); 308 309 return retval; 310 } 311