Home | History | Annotate | Download | only in ext2fs
      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(&param, 0, sizeof(param));
    160 	ext2fs_blocks_count_set(&param, blocks);
    161 	param.s_inodes_count = inodes;
    162 
    163 	retval = ext2fs_initialize("test fs", flags, &param,
    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