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