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
29extern ss_request_table tst_bitmaps_cmds;
30
31static char subsystem_name[] = "tst_bitmaps";
32static char version[] = "1.0";
33
34ext2_filsys	test_fs;
35int		exit_status = 0;
36
37static 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 */
105void 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 */
121unsigned 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
142int 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
151static 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
188errout:
189	ext2fs_close(test_fs);
190	test_fs = 0;
191}
192
193void 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
239void 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
249void 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
275void 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
284void 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
294void 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
332void 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
370void 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
405void 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
439void 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
448void 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
477void 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
506void 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
528void 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
562void 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
571int 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