tst_bitmaps.c revision 00eb0eee0addfd3b7ede98b85e00dff1547838a0
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, 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 189errout: 190 ext2fs_close(test_fs); 191 test_fs = 0; 192} 193 194void setup_cmd(int argc, char **argv) 195{ 196 int c, err; 197 unsigned int blocks = 128; 198 unsigned int inodes = 0; 199 unsigned int type = EXT2FS_BMAP64_BITARRAY; 200 int flags = EXT2_FLAG_64BITS; 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:lt:")) != 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 'l': /* Legacy bitmaps */ 223 flags = 0; 224 break; 225 case 't': 226 type = parse_ulong(optarg, argv[0], 227 "bitmap backend type", &err); 228 if (err) 229 return; 230 break; 231 default: 232 fprintf(stderr, "%s: usage: setup [-b blocks] " 233 "[-i inodes] [-t type]\n", argv[0]); 234 return; 235 } 236 } 237 setup_filesystem(argv[0], blocks, inodes, type, flags); 238} 239 240void close_cmd(int argc, char **argv) 241{ 242 if (check_fs_open(argv[0])) 243 return; 244 245 ext2fs_close(test_fs); 246 test_fs = 0; 247} 248 249 250void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num) 251{ 252 unsigned char *buf; 253 errcode_t retval; 254 int i, len = (num - start + 7) / 8; 255 256 buf = malloc(len); 257 if (!buf) { 258 com_err("dump_bitmap", 0, "couldn't allocate buffer"); 259 return; 260 } 261 memset(buf, 0, len); 262 retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf); 263 if (retval) { 264 com_err("dump_bitmap", retval, 265 "while calling ext2fs_generic_bmap_range"); 266 free(buf); 267 return; 268 } 269 for (i=0; i < len; i++) 270 printf("%02x", buf[i]); 271 printf("\n"); 272 printf("bits set: %u\n", ext2fs_bitcount(buf, len)); 273 free(buf); 274} 275 276void dump_inode_bitmap_cmd(int argc, char **argv) 277{ 278 if (check_fs_open(argv[0])) 279 return; 280 281 printf("inode bitmap: "); 282 dump_bitmap(test_fs->inode_map, 1, test_fs->super->s_inodes_count); 283} 284 285void dump_block_bitmap_cmd(int argc, char **argv) 286{ 287 if (check_fs_open(argv[0])) 288 return; 289 290 printf("block bitmap: "); 291 dump_bitmap(test_fs->block_map, test_fs->super->s_first_data_block, 292 test_fs->super->s_blocks_count); 293} 294 295void do_setb(int argc, char *argv[]) 296{ 297 unsigned int block, num; 298 int err; 299 int test_result, op_result; 300 301 if (check_fs_open(argv[0])) 302 return; 303 304 if (argc != 2 && argc != 3) { 305 com_err(argv[0], 0, "Usage: setb <block> [num]"); 306 return; 307 } 308 309 block = parse_ulong(argv[1], argv[0], "block", &err); 310 if (err) 311 return; 312 313 if (argc == 3) { 314 num = parse_ulong(argv[2], argv[0], "num", &err); 315 if (err) 316 return; 317 318 ext2fs_mark_block_bitmap_range2(test_fs->block_map, 319 block, num); 320 printf("Marking blocks %u to %u\n", block, block + num - 1); 321 return; 322 } 323 324 test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block); 325 op_result = ext2fs_mark_block_bitmap2(test_fs->block_map, block); 326 printf("Setting block %u, was %s before\n", block, op_result ? 327 "set" : "clear"); 328 if (!test_result != !op_result) 329 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", 330 test_result, op_result); 331} 332 333void do_clearb(int argc, char *argv[]) 334{ 335 unsigned int block, num; 336 int err; 337 int test_result, op_result; 338 339 if (check_fs_open(argv[0])) 340 return; 341 342 if (argc != 2 && argc != 3) { 343 com_err(argv[0], 0, "Usage: clearb <block> [num]"); 344 return; 345 } 346 347 block = parse_ulong(argv[1], argv[0], "block", &err); 348 if (err) 349 return; 350 351 if (argc == 3) { 352 num = parse_ulong(argv[2], argv[0], "num", &err); 353 if (err) 354 return; 355 356 ext2fs_unmark_block_bitmap_range2(test_fs->block_map, 357 block, num); 358 printf("Clearing blocks %u to %u\n", block, block + num - 1); 359 return; 360 } 361 362 test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block); 363 op_result = ext2fs_unmark_block_bitmap2(test_fs->block_map, block); 364 printf("Clearing block %u, was %s before\n", block, op_result ? 365 "set" : "clear"); 366 if (!test_result != !op_result) 367 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", 368 test_result, op_result); 369} 370 371void do_testb(int argc, char *argv[]) 372{ 373 unsigned int block, num; 374 int err; 375 int test_result; 376 377 if (check_fs_open(argv[0])) 378 return; 379 380 if (argc != 2 && argc != 3) { 381 com_err(argv[0], 0, "Usage: testb <block> [num]"); 382 return; 383 } 384 385 block = parse_ulong(argv[1], argv[0], "block", &err); 386 if (err) 387 return; 388 389 if (argc == 3) { 390 num = parse_ulong(argv[2], argv[0], "num", &err); 391 if (err) 392 return; 393 394 test_result = 395 ext2fs_test_block_bitmap_range2(test_fs->block_map, 396 block, num); 397 printf("Blocks %u to %u are %sall clear.\n", 398 block, block + num - 1, test_result ? "" : "NOT "); 399 return; 400 } 401 402 test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block); 403 printf("Block %u is %s\n", block, test_result ? "set" : "clear"); 404} 405 406void do_ffzb(int argc, char *argv[]) 407{ 408 unsigned int start, end; 409 int err; 410 errcode_t retval; 411 blk64_t out; 412 413 if (check_fs_open(argv[0])) 414 return; 415 416 if (argc != 3 && argc != 3) { 417 com_err(argv[0], 0, "Usage: ffzb <start> <end>"); 418 return; 419 } 420 421 start = parse_ulong(argv[1], argv[0], "start", &err); 422 if (err) 423 return; 424 425 end = parse_ulong(argv[2], argv[0], "end", &err); 426 if (err) 427 return; 428 429 retval = ext2fs_find_first_zero_block_bitmap2(test_fs->block_map, 430 start, end, &out); 431 if (retval) { 432 printf("ext2fs_find_first_zero_block_bitmap2() returned %s\n", 433 error_message(retval)); 434 return; 435 } 436 printf("First unmarked block is %llu\n", out); 437} 438 439 440void do_zerob(int argc, char *argv[]) 441{ 442 if (check_fs_open(argv[0])) 443 return; 444 445 printf("Clearing block bitmap.\n"); 446 ext2fs_clear_block_bitmap(test_fs->block_map); 447} 448 449void do_seti(int argc, char *argv[]) 450{ 451 unsigned int inode; 452 int err; 453 int test_result, op_result; 454 455 if (check_fs_open(argv[0])) 456 return; 457 458 if (argc != 2) { 459 com_err(argv[0], 0, "Usage: seti <inode>"); 460 return; 461 } 462 463 inode = parse_ulong(argv[1], argv[0], "inode", &err); 464 if (err) 465 return; 466 467 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode); 468 op_result = ext2fs_mark_inode_bitmap2(test_fs->inode_map, inode); 469 printf("Setting inode %u, was %s before\n", inode, op_result ? 470 "set" : "clear"); 471 if (!test_result != !op_result) { 472 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)", 473 test_result, op_result); 474 exit_status++; 475 } 476} 477 478void do_cleari(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: clearb <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_unmark_inode_bitmap2(test_fs->inode_map, inode); 498 printf("Clearing 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 507void do_testi(int argc, char *argv[]) 508{ 509 unsigned int inode; 510 int err; 511 int test_result; 512 513 if (check_fs_open(argv[0])) 514 return; 515 516 if (argc != 2) { 517 com_err(argv[0], 0, "Usage: testb <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 printf("Inode %u is %s\n", inode, test_result ? "set" : "clear"); 527} 528 529void do_ffzi(int argc, char *argv[]) 530{ 531 unsigned int start, end; 532 int err; 533 errcode_t retval; 534 ext2_ino_t out; 535 536 if (check_fs_open(argv[0])) 537 return; 538 539 if (argc != 3 && argc != 3) { 540 com_err(argv[0], 0, "Usage: ffzi <start> <end>"); 541 return; 542 } 543 544 start = parse_ulong(argv[1], argv[0], "start", &err); 545 if (err) 546 return; 547 548 end = parse_ulong(argv[2], argv[0], "end", &err); 549 if (err) 550 return; 551 552 retval = ext2fs_find_first_zero_inode_bitmap2(test_fs->inode_map, 553 start, end, &out); 554 if (retval) { 555 printf("ext2fs_find_first_zero_inode_bitmap2() returned %s\n", 556 error_message(retval)); 557 return; 558 } 559 printf("First unmarked inode is %u\n", out); 560} 561 562 563void do_zeroi(int argc, char *argv[]) 564{ 565 if (check_fs_open(argv[0])) 566 return; 567 568 printf("Clearing inode bitmap.\n"); 569 ext2fs_clear_inode_bitmap(test_fs->inode_map); 570} 571 572int main(int argc, char **argv) 573{ 574 unsigned int blocks = 128; 575 unsigned int inodes = 0; 576 unsigned int type = EXT2FS_BMAP64_BITARRAY; 577 int c, err, code; 578 char *request = (char *)NULL; 579 char *cmd_file = 0; 580 int sci_idx; 581 int flags = EXT2_FLAG_64BITS; 582 583 add_error_table(&et_ss_error_table); 584 add_error_table(&et_ext2_error_table); 585 while ((c = getopt (argc, argv, "b:i:lt:R:f:")) != EOF) { 586 switch (c) { 587 case 'b': 588 blocks = parse_ulong(optarg, argv[0], 589 "number of blocks", &err); 590 if (err) 591 exit(1); 592 break; 593 case 'i': 594 inodes = parse_ulong(optarg, argv[0], 595 "number of blocks", &err); 596 if (err) 597 exit(1); 598 break; 599 case 'l': /* Legacy bitmaps */ 600 flags = 0; 601 break; 602 case 't': 603 type = parse_ulong(optarg, argv[0], 604 "bitmap backend type", &err); 605 if (err) 606 exit(1); 607 break; 608 case 'R': 609 request = optarg; 610 break; 611 case 'f': 612 cmd_file = optarg; 613 break; 614 default: 615 com_err(argv[0], 0, "Usage: %s [-R request] " 616 "[-f cmd_file]", subsystem_name); 617 exit(1); 618 } 619 } 620 621 sci_idx = ss_create_invocation(subsystem_name, version, 622 (char *)NULL, &tst_bitmaps_cmds, &code); 623 if (code) { 624 ss_perror(sci_idx, code, "creating invocation"); 625 exit(1); 626 } 627 628 (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &code); 629 if (code) { 630 ss_perror(sci_idx, code, "adding standard requests"); 631 exit (1); 632 } 633 634 printf("%s %s. Type '?' for a list of commands.\n\n", 635 subsystem_name, version); 636 637 setup_filesystem(argv[0], blocks, inodes, type, flags); 638 639 if (request) { 640 code = ss_execute_line(sci_idx, request); 641 if (code) { 642 ss_perror(sci_idx, code, request); 643 exit_status++; 644 } 645 } else if (cmd_file) { 646 exit_status = source_file(cmd_file, sci_idx); 647 } else { 648 ss_listen(sci_idx); 649 } 650 651 exit(exit_status); 652} 653 654