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 30 extern ss_request_table tst_bitmaps_cmds; 31 32 static char subsystem_name[] = "tst_bitmaps"; 33 static char version[] = "1.0"; 34 35 ext2_filsys test_fs; 36 int exit_status = 0; 37 38 static 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 */ 106 void 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 */ 122 unsigned 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 143 int 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 152 static void setup_filesystem(const char *name, 153 unsigned int blocks, unsigned int inodes, 154 unsigned int type, int flags) 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", flags, ¶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 189 errout: 190 ext2fs_close_free(&test_fs); 191 } 192 193 void setup_cmd(int argc, char **argv) 194 { 195 int c, err; 196 unsigned int blocks = 128; 197 unsigned int inodes = 0; 198 unsigned int type = EXT2FS_BMAP64_BITARRAY; 199 int flags = EXT2_FLAG_64BITS; 200 201 if (test_fs) 202 ext2fs_close_free(&test_fs); 203 204 reset_getopt(); 205 while ((c = getopt(argc, argv, "b:i:lt:")) != EOF) { 206 switch (c) { 207 case 'b': 208 blocks = parse_ulong(optarg, argv[0], 209 "number of blocks", &err); 210 if (err) 211 return; 212 break; 213 case 'i': 214 inodes = parse_ulong(optarg, argv[0], 215 "number of blocks", &err); 216 if (err) 217 return; 218 break; 219 case 'l': /* Legacy bitmaps */ 220 flags = 0; 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, flags); 235 } 236 237 void close_cmd(int argc, char **argv) 238 { 239 if (check_fs_open(argv[0])) 240 return; 241 242 ext2fs_close_free(&test_fs); 243 } 244 245 246 void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num) 247 { 248 unsigned char *buf; 249 errcode_t retval; 250 int i, len = (num - start + 7) / 8; 251 252 buf = malloc(len); 253 if (!buf) { 254 com_err("dump_bitmap", 0, "couldn't allocate buffer"); 255 return; 256 } 257 memset(buf, 0, len); 258 retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf); 259 if (retval) { 260 com_err("dump_bitmap", retval, 261 "while calling ext2fs_generic_bmap_range"); 262 free(buf); 263 return; 264 } 265 for (i=0; i < len; i++) 266 printf("%02x", buf[i]); 267 printf("\n"); 268 printf("bits set: %u\n", ext2fs_bitcount(buf, len)); 269 free(buf); 270 } 271 272 void 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 281 void 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 291 void 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 329 void 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 367 void do_testb(int argc, char *argv[]) 368 { 369 unsigned int block, num; 370 int err; 371 int test_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 402 void do_ffzb(int argc, char *argv[]) 403 { 404 unsigned int start, end; 405 int err; 406 errcode_t retval; 407 blk64_t out; 408 409 if (check_fs_open(argv[0])) 410 return; 411 412 if (argc != 3 && argc != 3) { 413 com_err(argv[0], 0, "Usage: ffzb <start> <end>"); 414 return; 415 } 416 417 start = parse_ulong(argv[1], argv[0], "start", &err); 418 if (err) 419 return; 420 421 end = parse_ulong(argv[2], argv[0], "end", &err); 422 if (err) 423 return; 424 425 retval = ext2fs_find_first_zero_block_bitmap2(test_fs->block_map, 426 start, end, &out); 427 if (retval) { 428 printf("ext2fs_find_first_zero_block_bitmap2() returned %s\n", 429 error_message(retval)); 430 return; 431 } 432 printf("First unmarked block is %llu\n", out); 433 } 434 435 void do_ffsb(int argc, char *argv[]) 436 { 437 unsigned int start, end; 438 int err; 439 errcode_t retval; 440 blk64_t out; 441 442 if (check_fs_open(argv[0])) 443 return; 444 445 if (argc != 3 && argc != 3) { 446 com_err(argv[0], 0, "Usage: ffsb <start> <end>"); 447 return; 448 } 449 450 start = parse_ulong(argv[1], argv[0], "start", &err); 451 if (err) 452 return; 453 454 end = parse_ulong(argv[2], argv[0], "end", &err); 455 if (err) 456 return; 457 458 retval = ext2fs_find_first_set_block_bitmap2(test_fs->block_map, 459 start, end, &out); 460 if (retval) { 461 printf("ext2fs_find_first_set_block_bitmap2() returned %s\n", 462 error_message(retval)); 463 return; 464 } 465 printf("First marked block is %llu\n", out); 466 } 467 468 469 void do_zerob(int argc, char *argv[]) 470 { 471 if (check_fs_open(argv[0])) 472 return; 473 474 printf("Clearing block bitmap.\n"); 475 ext2fs_clear_block_bitmap(test_fs->block_map); 476 } 477 478 void do_seti(int argc, char *argv[]) 479 { 480 unsigned int inode; 481 int err; 482 int test_result, op_result; 483 484 if (check_fs_open(argv[0])) 485 return; 486 487 if (argc != 2) { 488 com_err(argv[0], 0, "Usage: seti <inode>"); 489 return; 490 } 491 492 inode = parse_ulong(argv[1], argv[0], "inode", &err); 493 if (err) 494 return; 495 496 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode); 497 op_result = ext2fs_mark_inode_bitmap2(test_fs->inode_map, inode); 498 printf("Setting inode %u, was %s before\n", inode, op_result ? 499 "set" : "clear"); 500 if (!test_result != !op_result) { 501 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", 502 test_result, op_result); 503 exit_status++; 504 } 505 } 506 507 void do_cleari(int argc, char *argv[]) 508 { 509 unsigned int inode; 510 int err; 511 int test_result, op_result; 512 513 if (check_fs_open(argv[0])) 514 return; 515 516 if (argc != 2) { 517 com_err(argv[0], 0, "Usage: clearb <inode>"); 518 return; 519 } 520 521 inode = parse_ulong(argv[1], argv[0], "inode", &err); 522 if (err) 523 return; 524 525 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode); 526 op_result = ext2fs_unmark_inode_bitmap2(test_fs->inode_map, inode); 527 printf("Clearing inode %u, was %s before\n", inode, op_result ? 528 "set" : "clear"); 529 if (!test_result != !op_result) { 530 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", 531 test_result, op_result); 532 exit_status++; 533 } 534 } 535 536 void do_testi(int argc, char *argv[]) 537 { 538 unsigned int inode; 539 int err; 540 int test_result; 541 542 if (check_fs_open(argv[0])) 543 return; 544 545 if (argc != 2) { 546 com_err(argv[0], 0, "Usage: testb <inode>"); 547 return; 548 } 549 550 inode = parse_ulong(argv[1], argv[0], "inode", &err); 551 if (err) 552 return; 553 554 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode); 555 printf("Inode %u is %s\n", inode, test_result ? "set" : "clear"); 556 } 557 558 void do_ffzi(int argc, char *argv[]) 559 { 560 unsigned int start, end; 561 int err; 562 errcode_t retval; 563 ext2_ino_t out; 564 565 if (check_fs_open(argv[0])) 566 return; 567 568 if (argc != 3 && argc != 3) { 569 com_err(argv[0], 0, "Usage: ffzi <start> <end>"); 570 return; 571 } 572 573 start = parse_ulong(argv[1], argv[0], "start", &err); 574 if (err) 575 return; 576 577 end = parse_ulong(argv[2], argv[0], "end", &err); 578 if (err) 579 return; 580 581 retval = ext2fs_find_first_zero_inode_bitmap2(test_fs->inode_map, 582 start, end, &out); 583 if (retval) { 584 printf("ext2fs_find_first_zero_inode_bitmap2() returned %s\n", 585 error_message(retval)); 586 return; 587 } 588 printf("First unmarked inode is %u\n", out); 589 } 590 591 void do_ffsi(int argc, char *argv[]) 592 { 593 unsigned int start, end; 594 int err; 595 errcode_t retval; 596 ext2_ino_t out; 597 598 if (check_fs_open(argv[0])) 599 return; 600 601 if (argc != 3 && argc != 3) { 602 com_err(argv[0], 0, "Usage: ffsi <start> <end>"); 603 return; 604 } 605 606 start = parse_ulong(argv[1], argv[0], "start", &err); 607 if (err) 608 return; 609 610 end = parse_ulong(argv[2], argv[0], "end", &err); 611 if (err) 612 return; 613 614 retval = ext2fs_find_first_set_inode_bitmap2(test_fs->inode_map, 615 start, end, &out); 616 if (retval) { 617 printf("ext2fs_find_first_set_inode_bitmap2() returned %s\n", 618 error_message(retval)); 619 return; 620 } 621 printf("First marked inode is %u\n", out); 622 } 623 624 void do_zeroi(int argc, char *argv[]) 625 { 626 if (check_fs_open(argv[0])) 627 return; 628 629 printf("Clearing inode bitmap.\n"); 630 ext2fs_clear_inode_bitmap(test_fs->inode_map); 631 } 632 633 int main(int argc, char **argv) 634 { 635 unsigned int blocks = 128; 636 unsigned int inodes = 0; 637 unsigned int type = EXT2FS_BMAP64_BITARRAY; 638 int c, err, code; 639 char *request = (char *)NULL; 640 char *cmd_file = 0; 641 int sci_idx; 642 int flags = EXT2_FLAG_64BITS; 643 644 add_error_table(&et_ss_error_table); 645 add_error_table(&et_ext2_error_table); 646 while ((c = getopt (argc, argv, "b:i:lt:R:f:")) != EOF) { 647 switch (c) { 648 case 'b': 649 blocks = parse_ulong(optarg, argv[0], 650 "number of blocks", &err); 651 if (err) 652 exit(1); 653 break; 654 case 'i': 655 inodes = parse_ulong(optarg, argv[0], 656 "number of blocks", &err); 657 if (err) 658 exit(1); 659 break; 660 case 'l': /* Legacy bitmaps */ 661 flags = 0; 662 break; 663 case 't': 664 type = parse_ulong(optarg, argv[0], 665 "bitmap backend type", &err); 666 if (err) 667 exit(1); 668 break; 669 case 'R': 670 request = optarg; 671 break; 672 case 'f': 673 cmd_file = optarg; 674 break; 675 default: 676 com_err(argv[0], 0, "Usage: %s [-R request] " 677 "[-f cmd_file]", subsystem_name); 678 exit(1); 679 } 680 } 681 682 sci_idx = ss_create_invocation(subsystem_name, version, 683 (char *)NULL, &tst_bitmaps_cmds, &code); 684 if (code) { 685 ss_perror(sci_idx, code, "creating invocation"); 686 exit(1); 687 } 688 689 (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &code); 690 if (code) { 691 ss_perror(sci_idx, code, "adding standard requests"); 692 exit (1); 693 } 694 695 printf("%s %s. Type '?' for a list of commands.\n\n", 696 subsystem_name, version); 697 698 setup_filesystem(argv[0], blocks, inodes, type, flags); 699 700 if (request) { 701 code = ss_execute_line(sci_idx, request); 702 if (code) { 703 ss_perror(sci_idx, code, request); 704 exit_status++; 705 } 706 } else if (cmd_file) { 707 exit_status = source_file(cmd_file, sci_idx); 708 } else { 709 ss_listen(sci_idx); 710 } 711 712 exit(exit_status); 713 } 714 715