tst_bitmaps.c revision b8ad88b3e746b0c37dfb61e0fe7ad7a7636f9537
1/* 2 * tst_bitmaps.c 3 * 4 * Copyright (C) 2011 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Library 8 * General Public License, version 2. 9 * %End-Header% 10 */ 11 12#include "config.h" 13#include <unistd.h> 14#include <stdlib.h> 15#include <stdio.h> 16#ifdef HAVE_GETOPT_H 17#include <getopt.h> 18#endif 19#include <string.h> 20#include <fcntl.h> 21#include <time.h> 22#include <sys/stat.h> 23#include <sys/types.h> 24#include "ss/ss.h" 25 26#include "ext2_fs.h" 27#include "ext2fs.h" 28#include "ext2fsP.h" 29 30extern ss_request_table tst_bitmaps_cmds; 31 32static char subsystem_name[] = "tst_bitmaps"; 33static char version[] = "1.0"; 34 35ext2_filsys test_fs; 36int exit_status = 0; 37 38static int source_file(const char *cmd_file, int sci_idx) 39{ 40 FILE *f; 41 char buf[256]; 42 char *cp; 43 int retval; 44 int noecho; 45 46 if (strcmp(cmd_file, "-") == 0) 47 f = stdin; 48 else { 49 f = fopen(cmd_file, "r"); 50 if (!f) { 51 perror(cmd_file); 52 exit(1); 53 } 54 } 55 fflush(stdout); 56 fflush(stderr); 57 setbuf(stdout, NULL); 58 setbuf(stderr, NULL); 59 while (!feof(f)) { 60 if (fgets(buf, sizeof(buf), f) == NULL) 61 break; 62 if (buf[0] == '#') 63 continue; 64 noecho = 0; 65 if (buf[0] == '-') { 66 noecho = 1; 67 buf[0] = ' '; 68 } 69 cp = strchr(buf, '\n'); 70 if (cp) 71 *cp = 0; 72 cp = strchr(buf, '\r'); 73 if (cp) 74 *cp = 0; 75 if (!noecho) 76 printf("%s: %s\n", subsystem_name, buf); 77 retval = ss_execute_line(sci_idx, buf); 78 if (retval) { 79 ss_perror(sci_idx, retval, buf); 80 exit_status++; 81 } 82 } 83 return exit_status; 84} 85 86 87/* 88 * This function resets the libc getopt() function, which keeps 89 * internal state. Bad design! Stupid libc API designers! No 90 * biscuit! 91 * 92 * BSD-derived getopt() functions require that optind be reset to 1 in 93 * order to reset getopt() state. This used to be generally accepted 94 * way of resetting getopt(). However, glibc's getopt() 95 * has additional getopt() state beyond optind, and requires that 96 * optind be set zero to reset its state. So the unfortunate state of 97 * affairs is that BSD-derived versions of getopt() misbehave if 98 * optind is set to 0 in order to reset getopt(), and glibc's getopt() 99 * will core dump if optind is set 1 in order to reset getopt(). 100 * 101 * More modern versions of BSD require that optreset be set to 1 in 102 * order to reset getopt(). Sigh. Standards, anyone? 103 * 104 * We hide the hair here. 105 */ 106void reset_getopt(void) 107{ 108#if defined(__GLIBC__) || defined(__linux__) 109 optind = 0; 110#else 111 optind = 1; 112#endif 113#ifdef HAVE_OPTRESET 114 optreset = 1; /* Makes BSD getopt happy */ 115#endif 116} 117 118/* 119 * This function will convert a string to an unsigned long, printing 120 * an error message if it fails, and returning success or failure in err. 121 */ 122unsigned long parse_ulong(const char *str, const char *cmd, 123 const char *descr, int *err) 124{ 125 char *tmp; 126 unsigned long ret; 127 128 ret = strtoul(str, &tmp, 0); 129 if (*tmp == 0) { 130 if (err) 131 *err = 0; 132 return ret; 133 } 134 com_err(cmd, 0, "Bad %s - %s", descr, str); 135 if (err) 136 *err = 1; 137 else 138 exit(1); 139 return 0; 140} 141 142 143int check_fs_open(char *name) 144{ 145 if (!test_fs) { 146 com_err(name, 0, "Filesystem not open"); 147 return 1; 148 } 149 return 0; 150} 151 152static void setup_filesystem(const char *name, 153 unsigned int blocks, unsigned int inodes, 154 unsigned int type) 155{ 156 struct ext2_super_block param; 157 errcode_t retval; 158 159 memset(¶m, 0, sizeof(param)); 160 ext2fs_blocks_count_set(¶m, blocks); 161 param.s_inodes_count = inodes; 162 163 retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, 164 test_io_manager, &test_fs); 165 166 if (retval) { 167 com_err(name, retval, "while initializing filesystem"); 168 return; 169 } 170 test_fs->default_bitmap_type = type; 171 ext2fs_free_block_bitmap(test_fs->block_map); 172 test_fs->block_map = 0; 173 ext2fs_free_inode_bitmap(test_fs->inode_map); 174 test_fs->inode_map = 0; 175 retval = ext2fs_allocate_block_bitmap(test_fs, "block bitmap", 176 &test_fs->block_map); 177 if (retval) { 178 com_err(name, retval, "while allocating block bitmap"); 179 goto errout; 180 } 181 retval = ext2fs_allocate_inode_bitmap(test_fs, "inode bitmap", 182 &test_fs->inode_map); 183 if (retval) { 184 com_err(name, retval, "while allocating inode bitmap"); 185 goto errout; 186 } 187 return; 188 189errout: 190 ext2fs_close(test_fs); 191 test_fs = 0; 192} 193 194void setup_cmd(int argc, char **argv) 195{ 196 errcode_t retval; 197 int i, c, err; 198 unsigned int blocks = 128; 199 unsigned int inodes = 0; 200 unsigned int type = EXT2FS_BMAP64_BITARRAY; 201 202 if (test_fs) { 203 ext2fs_close(test_fs); 204 test_fs = 0; 205 } 206 207 reset_getopt(); 208 while ((c = getopt(argc, argv, "b:i:t:")) != EOF) { 209 switch (c) { 210 case 'b': 211 blocks = parse_ulong(optarg, argv[0], 212 "number of blocks", &err); 213 if (err) 214 return; 215 break; 216 case 'i': 217 inodes = parse_ulong(optarg, argv[0], 218 "number of blocks", &err); 219 if (err) 220 return; 221 break; 222 case 't': 223 type = parse_ulong(optarg, argv[0], 224 "bitmap backend type", &err); 225 if (err) 226 return; 227 break; 228 default: 229 fprintf(stderr, "%s: usage: setup [-b blocks] " 230 "[-i inodes] [-t type]\n", argv[0]); 231 return; 232 } 233 } 234 setup_filesystem(argv[0], blocks, inodes, type); 235} 236 237void close_cmd(int argc, char **argv) 238{ 239 if (check_fs_open(argv[0])) 240 return; 241 242 ext2fs_close(test_fs); 243 test_fs = 0; 244} 245 246 247void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num) 248{ 249 unsigned char *buf; 250 errcode_t retval; 251 int i, len = (num - start + 7) / 8; 252 253 buf = malloc(len); 254 if (!buf) { 255 com_err("dump_bitmap", 0, "couldn't allocate buffer"); 256 return; 257 } 258 memset(buf, 0, len); 259 retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf); 260 if (retval) { 261 com_err("dump_bitmap", retval, 262 "while calling ext2fs_generic_bmap_range"); 263 free(buf); 264 return; 265 } 266 for (i=0; i < len; i++) 267 printf("%02x", buf[i]); 268 printf("\n"); 269 free(buf); 270} 271 272void dump_inode_bitmap_cmd(int argc, char **argv) 273{ 274 if (check_fs_open(argv[0])) 275 return; 276 277 printf("inode bitmap: "); 278 dump_bitmap(test_fs->inode_map, 1, test_fs->super->s_inodes_count); 279} 280 281void dump_block_bitmap_cmd(int argc, char **argv) 282{ 283 if (check_fs_open(argv[0])) 284 return; 285 286 printf("block bitmap: "); 287 dump_bitmap(test_fs->block_map, test_fs->super->s_first_data_block, 288 test_fs->super->s_blocks_count); 289} 290 291void do_setb(int argc, char *argv[]) 292{ 293 unsigned int block, num; 294 int err; 295 int test_result, op_result; 296 297 if (check_fs_open(argv[0])) 298 return; 299 300 if (argc != 2 && argc != 3) { 301 com_err(argv[0], 0, "Usage: setb <block> [num]"); 302 return; 303 } 304 305 block = parse_ulong(argv[1], argv[0], "block", &err); 306 if (err) 307 return; 308 309 if (argc == 3) { 310 num = parse_ulong(argv[2], argv[0], "num", &err); 311 if (err) 312 return; 313 314 ext2fs_mark_block_bitmap_range2(test_fs->block_map, 315 block, num); 316 printf("Marking blocks %u to %u\n", block, block + num - 1); 317 return; 318 } 319 320 test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block); 321 op_result = ext2fs_mark_block_bitmap2(test_fs->block_map, block); 322 printf("Setting block %u, was %s before\n", block, op_result ? 323 "set" : "clear"); 324 if (!test_result != !op_result) 325 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", 326 test_result, op_result); 327} 328 329void do_clearb(int argc, char *argv[]) 330{ 331 unsigned int block, num; 332 int err; 333 int test_result, op_result; 334 335 if (check_fs_open(argv[0])) 336 return; 337 338 if (argc != 2 && argc != 3) { 339 com_err(argv[0], 0, "Usage: clearb <block> [num]"); 340 return; 341 } 342 343 block = parse_ulong(argv[1], argv[0], "block", &err); 344 if (err) 345 return; 346 347 if (argc == 3) { 348 num = parse_ulong(argv[2], argv[0], "num", &err); 349 if (err) 350 return; 351 352 ext2fs_unmark_block_bitmap_range2(test_fs->block_map, 353 block, num); 354 printf("Clearing blocks %u to %u\n", block, block + num - 1); 355 return; 356 } 357 358 test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block); 359 op_result = ext2fs_unmark_block_bitmap2(test_fs->block_map, block); 360 printf("Clearing block %u, was %s before\n", block, op_result ? 361 "set" : "clear"); 362 if (!test_result != !op_result) 363 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", 364 test_result, op_result); 365} 366 367void do_testb(int argc, char *argv[]) 368{ 369 unsigned int block, num; 370 int err; 371 int test_result, op_result; 372 373 if (check_fs_open(argv[0])) 374 return; 375 376 if (argc != 2 && argc != 3) { 377 com_err(argv[0], 0, "Usage: testb <block> [num]"); 378 return; 379 } 380 381 block = parse_ulong(argv[1], argv[0], "block", &err); 382 if (err) 383 return; 384 385 if (argc == 3) { 386 num = parse_ulong(argv[2], argv[0], "num", &err); 387 if (err) 388 return; 389 390 test_result = 391 ext2fs_test_block_bitmap_range2(test_fs->block_map, 392 block, num); 393 printf("Blocks %u to %u are %sall clear.\n", 394 block, block + num - 1, test_result ? "" : "NOT "); 395 return; 396 } 397 398 test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block); 399 printf("Block %u is %s\n", block, test_result ? "set" : "clear"); 400} 401 402void do_zerob(int argc, char *argv[]) 403{ 404 if (check_fs_open(argv[0])) 405 return; 406 407 printf("Clearing block bitmap.\n"); 408 ext2fs_clear_block_bitmap(test_fs->block_map); 409} 410 411void do_seti(int argc, char *argv[]) 412{ 413 unsigned int inode; 414 int err; 415 int test_result, op_result; 416 417 if (check_fs_open(argv[0])) 418 return; 419 420 if (argc != 2) { 421 com_err(argv[0], 0, "Usage: seti <inode>"); 422 return; 423 } 424 425 inode = parse_ulong(argv[1], argv[0], "inode", &err); 426 if (err) 427 return; 428 429 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode); 430 op_result = ext2fs_mark_inode_bitmap2(test_fs->inode_map, inode); 431 printf("Setting inode %u, was %s before\n", inode, op_result ? 432 "set" : "clear"); 433 if (!test_result != !op_result) { 434 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", 435 test_result, op_result); 436 exit_status++; 437 } 438} 439 440void do_cleari(int argc, char *argv[]) 441{ 442 unsigned int inode; 443 int err; 444 int test_result, op_result; 445 446 if (check_fs_open(argv[0])) 447 return; 448 449 if (argc != 2) { 450 com_err(argv[0], 0, "Usage: clearb <inode>"); 451 return; 452 } 453 454 inode = parse_ulong(argv[1], argv[0], "inode", &err); 455 if (err) 456 return; 457 458 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode); 459 op_result = ext2fs_unmark_inode_bitmap2(test_fs->inode_map, inode); 460 printf("Clearing inode %u, was %s before\n", inode, op_result ? 461 "set" : "clear"); 462 if (!test_result != !op_result) { 463 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", 464 test_result, op_result); 465 exit_status++; 466 } 467} 468 469void do_testi(int argc, char *argv[]) 470{ 471 unsigned int inode; 472 int err; 473 int test_result, op_result; 474 475 if (check_fs_open(argv[0])) 476 return; 477 478 if (argc != 2) { 479 com_err(argv[0], 0, "Usage: testb <inode>"); 480 return; 481 } 482 483 inode = parse_ulong(argv[1], argv[0], "inode", &err); 484 if (err) 485 return; 486 487 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode); 488 printf("Inode %u is %s\n", inode, test_result ? "set" : "clear"); 489} 490 491void do_zeroi(int argc, char *argv[]) 492{ 493 if (check_fs_open(argv[0])) 494 return; 495 496 printf("Clearing inode bitmap.\n"); 497 ext2fs_clear_inode_bitmap(test_fs->inode_map); 498} 499 500int main(int argc, char **argv) 501{ 502 unsigned int blocks = 128; 503 unsigned int inodes = 0; 504 unsigned int type = EXT2FS_BMAP64_BITARRAY; 505 int c, err, code; 506 char *request = (char *)NULL; 507 char *cmd_file = 0; 508 int sci_idx; 509 510 add_error_table(&et_ss_error_table); 511 add_error_table(&et_ext2_error_table); 512 while ((c = getopt (argc, argv, "b:i:t:R:f:")) != EOF) { 513 switch (c) { 514 case 'b': 515 blocks = parse_ulong(optarg, argv[0], 516 "number of blocks", &err); 517 if (err) 518 return; 519 break; 520 case 'i': 521 inodes = parse_ulong(optarg, argv[0], 522 "number of blocks", &err); 523 if (err) 524 return; 525 break; 526 case 't': 527 type = parse_ulong(optarg, argv[0], 528 "bitmap backend type", &err); 529 if (err) 530 return; 531 break; 532 case 'R': 533 request = optarg; 534 break; 535 case 'f': 536 cmd_file = optarg; 537 break; 538 default: 539 com_err(argv[0], 0, "Usage: %s [-R request] " 540 "[-f cmd_file]", subsystem_name); 541 exit(1); 542 } 543 } 544 545 sci_idx = ss_create_invocation(subsystem_name, version, 546 (char *)NULL, &tst_bitmaps_cmds, &code); 547 if (code) { 548 ss_perror(sci_idx, code, "creating invocation"); 549 exit(1); 550 } 551 552 (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &code); 553 if (code) { 554 ss_perror(sci_idx, code, "adding standard requests"); 555 exit (1); 556 } 557 558 printf("%s %s. Type '?' for a list of commands.\n\n", 559 subsystem_name, version); 560 561 setup_filesystem(argv[0], blocks, inodes, type); 562 563 if (request) { 564 code = ss_execute_line(sci_idx, request); 565 if (code) { 566 ss_perror(sci_idx, code, request); 567 exit_status++; 568 } 569 } else if (cmd_file) { 570 exit_status = source_file(cmd_file, sci_idx); 571 } else { 572 ss_listen(sci_idx); 573 } 574 575 exit(exit_status); 576} 577 578