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 <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(&param, 0, sizeof(param));
    159 	ext2fs_blocks_count_set(&param, blocks);
    160 	param.s_inodes_count = inodes;
    161 
    162 	retval = ext2fs_initialize("test fs", flags, &param,
    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