tst_bitmaps.c revision b8ad88b3e746b0c37dfb61e0fe7ad7a7636f9537
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)
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", EXT2_FLAG_64BITS, &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
189errout:
190	ext2fs_close(test_fs);
191	test_fs = 0;
192}
193
194void setup_cmd(int argc, char **argv)
195{
196	errcode_t	retval;
197	int		i, c, err;
198	unsigned int	blocks = 128;
199	unsigned int	inodes = 0;
200	unsigned int	type = EXT2FS_BMAP64_BITARRAY;
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:t:")) != 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 '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);
235}
236
237void close_cmd(int argc, char **argv)
238{
239	if (check_fs_open(argv[0]))
240		return;
241
242	ext2fs_close(test_fs);
243	test_fs = 0;
244}
245
246
247void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num)
248{
249	unsigned char	*buf;
250	errcode_t	retval;
251	int		i, len = (num - start + 7) / 8;
252
253	buf = malloc(len);
254	if (!buf) {
255		com_err("dump_bitmap", 0, "couldn't allocate buffer");
256		return;
257	}
258	memset(buf, 0, len);
259	retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf);
260	if (retval) {
261		com_err("dump_bitmap", retval,
262			"while calling ext2fs_generic_bmap_range");
263		free(buf);
264		return;
265	}
266	for (i=0; i < len; i++)
267		printf("%02x", buf[i]);
268	printf("\n");
269	free(buf);
270}
271
272void 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
281void 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
291void 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
329void 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
367void do_testb(int argc, char *argv[])
368{
369	unsigned int block, num;
370	int err;
371	int test_result, op_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
402void do_zerob(int argc, char *argv[])
403{
404	if (check_fs_open(argv[0]))
405		return;
406
407	printf("Clearing block bitmap.\n");
408	ext2fs_clear_block_bitmap(test_fs->block_map);
409}
410
411void do_seti(int argc, char *argv[])
412{
413	unsigned int inode;
414	int err;
415	int test_result, op_result;
416
417	if (check_fs_open(argv[0]))
418		return;
419
420	if (argc != 2) {
421		com_err(argv[0], 0, "Usage: seti <inode>");
422		return;
423	}
424
425	inode = parse_ulong(argv[1], argv[0], "inode", &err);
426	if (err)
427		return;
428
429	test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode);
430	op_result = ext2fs_mark_inode_bitmap2(test_fs->inode_map, inode);
431	printf("Setting inode %u, was %s before\n", inode, op_result ?
432	       "set" : "clear");
433	if (!test_result != !op_result) {
434		com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
435			test_result, op_result);
436		exit_status++;
437	}
438}
439
440void do_cleari(int argc, char *argv[])
441{
442	unsigned int inode;
443	int err;
444	int test_result, op_result;
445
446	if (check_fs_open(argv[0]))
447		return;
448
449	if (argc != 2) {
450		com_err(argv[0], 0, "Usage: clearb <inode>");
451		return;
452	}
453
454	inode = parse_ulong(argv[1], argv[0], "inode", &err);
455	if (err)
456		return;
457
458	test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode);
459	op_result = ext2fs_unmark_inode_bitmap2(test_fs->inode_map, inode);
460	printf("Clearing inode %u, was %s before\n", inode, op_result ?
461	       "set" : "clear");
462	if (!test_result != !op_result) {
463		com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
464			test_result, op_result);
465		exit_status++;
466	}
467}
468
469void do_testi(int argc, char *argv[])
470{
471	unsigned int inode;
472	int err;
473	int test_result, op_result;
474
475	if (check_fs_open(argv[0]))
476		return;
477
478	if (argc != 2) {
479		com_err(argv[0], 0, "Usage: testb <inode>");
480		return;
481	}
482
483	inode = parse_ulong(argv[1], argv[0], "inode", &err);
484	if (err)
485		return;
486
487	test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode);
488	printf("Inode %u is %s\n", inode, test_result ? "set" : "clear");
489}
490
491void do_zeroi(int argc, char *argv[])
492{
493	if (check_fs_open(argv[0]))
494		return;
495
496	printf("Clearing inode bitmap.\n");
497	ext2fs_clear_inode_bitmap(test_fs->inode_map);
498}
499
500int main(int argc, char **argv)
501{
502	unsigned int	blocks = 128;
503	unsigned int	inodes = 0;
504	unsigned int	type = EXT2FS_BMAP64_BITARRAY;
505	int		c, err, code;
506	char		*request = (char *)NULL;
507	char		*cmd_file = 0;
508	int		sci_idx;
509
510	add_error_table(&et_ss_error_table);
511	add_error_table(&et_ext2_error_table);
512	while ((c = getopt (argc, argv, "b:i:t:R:f:")) != EOF) {
513		switch (c) {
514		case 'b':
515			blocks = parse_ulong(optarg, argv[0],
516					     "number of blocks", &err);
517			if (err)
518				return;
519			break;
520		case 'i':
521			inodes = parse_ulong(optarg, argv[0],
522					     "number of blocks", &err);
523			if (err)
524				return;
525			break;
526		case 't':
527			type = parse_ulong(optarg, argv[0],
528					   "bitmap backend type", &err);
529			if (err)
530				return;
531			break;
532		case 'R':
533			request = optarg;
534			break;
535		case 'f':
536			cmd_file = optarg;
537			break;
538		default:
539			com_err(argv[0], 0, "Usage: %s [-R request] "
540				"[-f cmd_file]", subsystem_name);
541			exit(1);
542		}
543	}
544
545	sci_idx = ss_create_invocation(subsystem_name, version,
546				       (char *)NULL, &tst_bitmaps_cmds, &code);
547	if (code) {
548		ss_perror(sci_idx, code, "creating invocation");
549		exit(1);
550	}
551
552	(void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &code);
553	if (code) {
554		ss_perror(sci_idx, code, "adding standard requests");
555		exit (1);
556	}
557
558	printf("%s %s.  Type '?' for a list of commands.\n\n",
559	       subsystem_name, version);
560
561	setup_filesystem(argv[0], blocks, inodes, type);
562
563	if (request) {
564		code = ss_execute_line(sci_idx, request);
565		if (code) {
566			ss_perror(sci_idx, code, request);
567			exit_status++;
568		}
569	} else if (cmd_file) {
570		exit_status = source_file(cmd_file, sci_idx);
571	} else {
572		ss_listen(sci_idx);
573	}
574
575	exit(exit_status);
576}
577
578