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
source_file(const char * cmd_file,int sci_idx)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 */
reset_getopt(void)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 */
parse_ulong(const char * str,const char * cmd,const char * descr,int * err)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
check_fs_open(char * name)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
setup_filesystem(const char * name,unsigned int blocks,unsigned int inodes,unsigned int type,int flags)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(¶m, 0, sizeof(param));
160 ext2fs_blocks_count_set(¶m, blocks);
161 param.s_inodes_count = inodes;
162
163 retval = ext2fs_initialize("test fs", flags, ¶m,
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
setup_cmd(int argc,char ** argv,int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))193 void setup_cmd(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)),
194 void *infop EXT2FS_ATTR((unused)))
195 {
196 int c, err;
197 unsigned int blocks = 128;
198 unsigned int inodes = 0;
199 unsigned int type = EXT2FS_BMAP64_BITARRAY;
200 int flags = EXT2_FLAG_64BITS;
201
202 if (test_fs)
203 ext2fs_close_free(&test_fs);
204
205 reset_getopt();
206 while ((c = getopt(argc, argv, "b:i:lt:")) != EOF) {
207 switch (c) {
208 case 'b':
209 blocks = parse_ulong(optarg, argv[0],
210 "number of blocks", &err);
211 if (err)
212 return;
213 break;
214 case 'i':
215 inodes = parse_ulong(optarg, argv[0],
216 "number of blocks", &err);
217 if (err)
218 return;
219 break;
220 case 'l': /* Legacy bitmaps */
221 flags = 0;
222 break;
223 case 't':
224 type = parse_ulong(optarg, argv[0],
225 "bitmap backend type", &err);
226 if (err)
227 return;
228 break;
229 default:
230 fprintf(stderr, "%s: usage: setup [-b blocks] "
231 "[-i inodes] [-t type]\n", argv[0]);
232 return;
233 }
234 }
235 setup_filesystem(argv[0], blocks, inodes, type, flags);
236 }
237
close_cmd(int argc,char ** argv,int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))238 void close_cmd(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)),
239 void *infop EXT2FS_ATTR((unused)))
240 {
241 if (check_fs_open(argv[0]))
242 return;
243
244 ext2fs_close_free(&test_fs);
245 }
246
247
dump_bitmap(ext2fs_generic_bitmap bmap,unsigned int start,unsigned num)248 void dump_bitmap(ext2fs_generic_bitmap bmap, unsigned int start, unsigned num)
249 {
250 unsigned char *buf;
251 errcode_t retval;
252 int i, len = (num - start + 7) / 8;
253
254 buf = malloc(len);
255 if (!buf) {
256 com_err("dump_bitmap", 0, "couldn't allocate buffer");
257 return;
258 }
259 memset(buf, 0, len);
260 retval = ext2fs_get_generic_bmap_range(bmap, (__u64) start, num, buf);
261 if (retval) {
262 com_err("dump_bitmap", retval,
263 "while calling ext2fs_generic_bmap_range");
264 free(buf);
265 return;
266 }
267 for (i=0; i < len; i++)
268 printf("%02x", buf[i]);
269 printf("\n");
270 printf("bits set: %u\n", ext2fs_bitcount(buf, len));
271 free(buf);
272 }
273
dump_inode_bitmap_cmd(int argc,char ** argv,int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))274 void dump_inode_bitmap_cmd(int argc, char **argv,
275 int sci_idx EXT2FS_ATTR((unused)),
276 void *infop EXT2FS_ATTR((unused)))
277 {
278 if (check_fs_open(argv[0]))
279 return;
280
281 printf("inode bitmap: ");
282 dump_bitmap(test_fs->inode_map, 1, test_fs->super->s_inodes_count);
283 }
284
dump_block_bitmap_cmd(int argc,char ** argv,int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))285 void dump_block_bitmap_cmd(int argc, char **argv,
286 int sci_idx EXT2FS_ATTR((unused)),
287 void *infop EXT2FS_ATTR((unused)))
288 {
289 if (check_fs_open(argv[0]))
290 return;
291
292 printf("block bitmap: ");
293 dump_bitmap(test_fs->block_map, test_fs->super->s_first_data_block,
294 test_fs->super->s_blocks_count);
295 }
296
do_setb(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))297 void do_setb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
298 void *infop EXT2FS_ATTR((unused)))
299 {
300 unsigned int block, num;
301 int err;
302 int test_result, op_result;
303
304 if (check_fs_open(argv[0]))
305 return;
306
307 if (argc != 2 && argc != 3) {
308 com_err(argv[0], 0, "Usage: setb <block> [num]");
309 return;
310 }
311
312 block = parse_ulong(argv[1], argv[0], "block", &err);
313 if (err)
314 return;
315
316 if (argc == 3) {
317 num = parse_ulong(argv[2], argv[0], "num", &err);
318 if (err)
319 return;
320
321 ext2fs_mark_block_bitmap_range2(test_fs->block_map,
322 block, num);
323 printf("Marking blocks %u to %u\n", block, block + num - 1);
324 return;
325 }
326
327 test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block);
328 op_result = ext2fs_mark_block_bitmap2(test_fs->block_map, block);
329 printf("Setting block %u, was %s before\n", block, op_result ?
330 "set" : "clear");
331 if (!test_result != !op_result)
332 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
333 test_result, op_result);
334 }
335
do_clearb(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))336 void do_clearb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
337 void *infop EXT2FS_ATTR((unused)))
338 {
339 unsigned int block, num;
340 int err;
341 int test_result, op_result;
342
343 if (check_fs_open(argv[0]))
344 return;
345
346 if (argc != 2 && argc != 3) {
347 com_err(argv[0], 0, "Usage: clearb <block> [num]");
348 return;
349 }
350
351 block = parse_ulong(argv[1], argv[0], "block", &err);
352 if (err)
353 return;
354
355 if (argc == 3) {
356 num = parse_ulong(argv[2], argv[0], "num", &err);
357 if (err)
358 return;
359
360 ext2fs_unmark_block_bitmap_range2(test_fs->block_map,
361 block, num);
362 printf("Clearing blocks %u to %u\n", block, block + num - 1);
363 return;
364 }
365
366 test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block);
367 op_result = ext2fs_unmark_block_bitmap2(test_fs->block_map, block);
368 printf("Clearing block %u, was %s before\n", block, op_result ?
369 "set" : "clear");
370 if (!test_result != !op_result)
371 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
372 test_result, op_result);
373 }
374
do_testb(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))375 void do_testb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
376 void *infop EXT2FS_ATTR((unused)))
377 {
378 unsigned int block, num;
379 int err;
380 int test_result;
381
382 if (check_fs_open(argv[0]))
383 return;
384
385 if (argc != 2 && argc != 3) {
386 com_err(argv[0], 0, "Usage: testb <block> [num]");
387 return;
388 }
389
390 block = parse_ulong(argv[1], argv[0], "block", &err);
391 if (err)
392 return;
393
394 if (argc == 3) {
395 num = parse_ulong(argv[2], argv[0], "num", &err);
396 if (err)
397 return;
398
399 test_result =
400 ext2fs_test_block_bitmap_range2(test_fs->block_map,
401 block, num);
402 printf("Blocks %u to %u are %sall clear.\n",
403 block, block + num - 1, test_result ? "" : "NOT ");
404 return;
405 }
406
407 test_result = ext2fs_test_block_bitmap2(test_fs->block_map, block);
408 printf("Block %u is %s\n", block, test_result ? "set" : "clear");
409 }
410
do_ffzb(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))411 void do_ffzb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
412 void *infop EXT2FS_ATTR((unused)))
413 {
414 unsigned int start, end;
415 int err;
416 errcode_t retval;
417 blk64_t out;
418
419 if (check_fs_open(argv[0]))
420 return;
421
422 if (argc != 3 && argc != 3) {
423 com_err(argv[0], 0, "Usage: ffzb <start> <end>");
424 return;
425 }
426
427 start = parse_ulong(argv[1], argv[0], "start", &err);
428 if (err)
429 return;
430
431 end = parse_ulong(argv[2], argv[0], "end", &err);
432 if (err)
433 return;
434
435 retval = ext2fs_find_first_zero_block_bitmap2(test_fs->block_map,
436 start, end, &out);
437 if (retval) {
438 printf("ext2fs_find_first_zero_block_bitmap2() returned %s\n",
439 error_message(retval));
440 return;
441 }
442 printf("First unmarked block is %llu\n", out);
443 }
444
do_ffsb(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))445 void do_ffsb(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
446 void *infop EXT2FS_ATTR((unused)))
447 {
448 unsigned int start, end;
449 int err;
450 errcode_t retval;
451 blk64_t out;
452
453 if (check_fs_open(argv[0]))
454 return;
455
456 if (argc != 3 && argc != 3) {
457 com_err(argv[0], 0, "Usage: ffsb <start> <end>");
458 return;
459 }
460
461 start = parse_ulong(argv[1], argv[0], "start", &err);
462 if (err)
463 return;
464
465 end = parse_ulong(argv[2], argv[0], "end", &err);
466 if (err)
467 return;
468
469 retval = ext2fs_find_first_set_block_bitmap2(test_fs->block_map,
470 start, end, &out);
471 if (retval) {
472 printf("ext2fs_find_first_set_block_bitmap2() returned %s\n",
473 error_message(retval));
474 return;
475 }
476 printf("First marked block is %llu\n", out);
477 }
478
479
do_zerob(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))480 void do_zerob(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
481 void *infop EXT2FS_ATTR((unused)))
482 {
483 if (check_fs_open(argv[0]))
484 return;
485
486 printf("Clearing block bitmap.\n");
487 ext2fs_clear_block_bitmap(test_fs->block_map);
488 }
489
do_seti(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))490 void do_seti(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
491 void *infop EXT2FS_ATTR((unused)))
492 {
493 unsigned int inode;
494 int err;
495 int test_result, op_result;
496
497 if (check_fs_open(argv[0]))
498 return;
499
500 if (argc != 2) {
501 com_err(argv[0], 0, "Usage: seti <inode>");
502 return;
503 }
504
505 inode = parse_ulong(argv[1], argv[0], "inode", &err);
506 if (err)
507 return;
508
509 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode);
510 op_result = ext2fs_mark_inode_bitmap2(test_fs->inode_map, inode);
511 printf("Setting inode %u, was %s before\n", inode, op_result ?
512 "set" : "clear");
513 if (!test_result != !op_result) {
514 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
515 test_result, op_result);
516 exit_status++;
517 }
518 }
519
do_cleari(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))520 void do_cleari(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
521 void *infop EXT2FS_ATTR((unused)))
522 {
523 unsigned int inode;
524 int err;
525 int test_result, op_result;
526
527 if (check_fs_open(argv[0]))
528 return;
529
530 if (argc != 2) {
531 com_err(argv[0], 0, "Usage: clearb <inode>");
532 return;
533 }
534
535 inode = parse_ulong(argv[1], argv[0], "inode", &err);
536 if (err)
537 return;
538
539 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode);
540 op_result = ext2fs_unmark_inode_bitmap2(test_fs->inode_map, inode);
541 printf("Clearing inode %u, was %s before\n", inode, op_result ?
542 "set" : "clear");
543 if (!test_result != !op_result) {
544 com_err(argv[0], 0, "*ERROR* test_result different! (%d, %d)",
545 test_result, op_result);
546 exit_status++;
547 }
548 }
549
do_testi(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))550 void do_testi(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
551 void *infop EXT2FS_ATTR((unused)))
552 {
553 unsigned int inode;
554 int err;
555 int test_result;
556
557 if (check_fs_open(argv[0]))
558 return;
559
560 if (argc != 2) {
561 com_err(argv[0], 0, "Usage: testb <inode>");
562 return;
563 }
564
565 inode = parse_ulong(argv[1], argv[0], "inode", &err);
566 if (err)
567 return;
568
569 test_result = ext2fs_test_inode_bitmap2(test_fs->inode_map, inode);
570 printf("Inode %u is %s\n", inode, test_result ? "set" : "clear");
571 }
572
do_ffzi(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))573 void do_ffzi(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
574 void *infop EXT2FS_ATTR((unused)))
575 {
576 unsigned int start, end;
577 int err;
578 errcode_t retval;
579 ext2_ino_t out;
580
581 if (check_fs_open(argv[0]))
582 return;
583
584 if (argc != 3 && argc != 3) {
585 com_err(argv[0], 0, "Usage: ffzi <start> <end>");
586 return;
587 }
588
589 start = parse_ulong(argv[1], argv[0], "start", &err);
590 if (err)
591 return;
592
593 end = parse_ulong(argv[2], argv[0], "end", &err);
594 if (err)
595 return;
596
597 retval = ext2fs_find_first_zero_inode_bitmap2(test_fs->inode_map,
598 start, end, &out);
599 if (retval) {
600 printf("ext2fs_find_first_zero_inode_bitmap2() returned %s\n",
601 error_message(retval));
602 return;
603 }
604 printf("First unmarked inode is %u\n", out);
605 }
606
do_ffsi(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))607 void do_ffsi(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
608 void *infop EXT2FS_ATTR((unused)))
609 {
610 unsigned int start, end;
611 int err;
612 errcode_t retval;
613 ext2_ino_t out;
614
615 if (check_fs_open(argv[0]))
616 return;
617
618 if (argc != 3 && argc != 3) {
619 com_err(argv[0], 0, "Usage: ffsi <start> <end>");
620 return;
621 }
622
623 start = parse_ulong(argv[1], argv[0], "start", &err);
624 if (err)
625 return;
626
627 end = parse_ulong(argv[2], argv[0], "end", &err);
628 if (err)
629 return;
630
631 retval = ext2fs_find_first_set_inode_bitmap2(test_fs->inode_map,
632 start, end, &out);
633 if (retval) {
634 printf("ext2fs_find_first_set_inode_bitmap2() returned %s\n",
635 error_message(retval));
636 return;
637 }
638 printf("First marked inode is %u\n", out);
639 }
640
do_zeroi(int argc,char * argv[],int sci_idx EXT2FS_ATTR ((unused)),void * infop EXT2FS_ATTR ((unused)))641 void do_zeroi(int argc, char *argv[], int sci_idx EXT2FS_ATTR((unused)),
642 void *infop EXT2FS_ATTR((unused)))
643 {
644 if (check_fs_open(argv[0]))
645 return;
646
647 printf("Clearing inode bitmap.\n");
648 ext2fs_clear_inode_bitmap(test_fs->inode_map);
649 }
650
main(int argc,char ** argv)651 int main(int argc, char **argv)
652 {
653 unsigned int blocks = 128;
654 unsigned int inodes = 0;
655 unsigned int type = EXT2FS_BMAP64_BITARRAY;
656 int c, err, code;
657 char *request = (char *)NULL;
658 char *cmd_file = 0;
659 int sci_idx;
660 int flags = EXT2_FLAG_64BITS;
661
662 add_error_table(&et_ss_error_table);
663 add_error_table(&et_ext2_error_table);
664 while ((c = getopt (argc, argv, "b:i:lt:R:f:")) != EOF) {
665 switch (c) {
666 case 'b':
667 blocks = parse_ulong(optarg, argv[0],
668 "number of blocks", &err);
669 if (err)
670 exit(1);
671 break;
672 case 'i':
673 inodes = parse_ulong(optarg, argv[0],
674 "number of blocks", &err);
675 if (err)
676 exit(1);
677 break;
678 case 'l': /* Legacy bitmaps */
679 flags = 0;
680 break;
681 case 't':
682 type = parse_ulong(optarg, argv[0],
683 "bitmap backend type", &err);
684 if (err)
685 exit(1);
686 break;
687 case 'R':
688 request = optarg;
689 break;
690 case 'f':
691 cmd_file = optarg;
692 break;
693 default:
694 com_err(argv[0], 0, "Usage: %s [-R request] "
695 "[-f cmd_file]", subsystem_name);
696 exit(1);
697 }
698 }
699
700 sci_idx = ss_create_invocation(subsystem_name, version,
701 (char *)NULL, &tst_bitmaps_cmds, &code);
702 if (code) {
703 ss_perror(sci_idx, code, "creating invocation");
704 exit(1);
705 }
706
707 (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &code);
708 if (code) {
709 ss_perror(sci_idx, code, "adding standard requests");
710 exit (1);
711 }
712
713 printf("%s %s. Type '?' for a list of commands.\n\n",
714 subsystem_name, version);
715
716 setup_filesystem(argv[0], blocks, inodes, type, flags);
717
718 if (request) {
719 code = ss_execute_line(sci_idx, request);
720 if (code) {
721 ss_perror(sci_idx, code, request);
722 exit_status++;
723 }
724 } else if (cmd_file) {
725 exit_status = source_file(cmd_file, sci_idx);
726 } else {
727 ss_listen(sci_idx);
728 }
729
730 exit(exit_status);
731 }
732
733