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
source_file(const char * cmd_file,int sci_idx)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 */
reset_getopt(void)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 */
parse_ulong(const char * str,const char * cmd,const char * descr,int * err)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
check_fs_open(char * name)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
setup_filesystem(const char * name,unsigned int blocks,unsigned int inodes,unsigned int type,int flags)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(¶m, 0, sizeof(param));
159 ext2fs_blocks_count_set(¶m, blocks);
160 param.s_inodes_count = inodes;
161
162 retval = ext2fs_initialize("test fs", flags, ¶m,
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
setup_cmd(int argc,char ** argv)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
close_cmd(int argc,char ** argv)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
dump_bitmap(ext2fs_generic_bitmap bmap,unsigned int start,unsigned num)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
dump_inode_bitmap_cmd(int argc,char ** argv)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
dump_block_bitmap_cmd(int argc,char ** argv)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
do_setb(int argc,char * argv[])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
do_clearb(int argc,char * argv[])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
do_testb(int argc,char * argv[])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
do_ffzb(int argc,char * argv[])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
do_zerob(int argc,char * argv[])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
do_seti(int argc,char * argv[])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
do_cleari(int argc,char * argv[])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
do_testi(int argc,char * argv[])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
do_ffzi(int argc,char * argv[])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
do_zeroi(int argc,char * argv[])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
main(int argc,char ** argv)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