Whamcloud - gitweb
Add a new command "bmap" to debugfs which calculates the logical->physical block
[tools/e2fsprogs.git] / debugfs / debugfs.c
1 /*
2  * debugfs.c --- a program which allows you to attach an ext2fs
3  * filesystem and play with it.
4  *
5  * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
6  * under the terms of the GNU Public License.
7  * 
8  * Modifications by Robert Sanders <gt8134b@prism.gatech.edu>
9  */
10
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <string.h>
16 #include <time.h>
17 #ifdef HAVE_GETOPT_H
18 #include <getopt.h>
19 #else 
20 extern int optind;
21 extern char *optarg;
22 #endif
23 #ifdef HAVE_OPTRESET
24 extern int optreset;            /* defined by BSD, but not others */
25 #endif
26 #ifdef HAVE_ERRNO_H
27 #include <errno.h>
28 #endif
29 #include <fcntl.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32
33 #include "et/com_err.h"
34 #include "ss/ss.h"
35 #include "debugfs.h"
36 #include "uuid/uuid.h"
37 #include "e2p/e2p.h"
38
39 #include "../version.h"
40
41 extern ss_request_table debug_cmds;
42
43 ext2_filsys     current_fs = NULL;
44 ext2_ino_t      root, cwd;
45
46 static void open_filesystem(char *device, int open_flags, blk_t superblock,
47                             blk_t blocksize, int catastrophic)
48 {
49         int     retval;
50
51         if (superblock != 0 && blocksize == 0) {
52                 com_err(device, 0, "if you specify the superblock, you must also specify the block size");
53                 current_fs = NULL;
54                 return;
55         }
56
57         if (catastrophic && (open_flags & EXT2_FLAG_RW)) {
58                 com_err(device, 0,
59                         "opening read-only because of catastrophic mode");
60                 open_flags &= ~EXT2_FLAG_RW;
61         }
62         
63         retval = ext2fs_open(device, open_flags, superblock, blocksize,
64                              unix_io_manager, &current_fs);
65         if (retval) {
66                 com_err(device, retval, "while opening filesystem");
67                 current_fs = NULL;
68                 return;
69         }
70
71         if (catastrophic)
72                 com_err(device, 0, "catastrophic mode - not reading inode or group bitmaps");
73         else {
74                 retval = ext2fs_read_inode_bitmap(current_fs);
75                 if (retval) {
76                         com_err(device, retval, "while reading inode bitmap");
77                         goto errout;
78                 }
79                 retval = ext2fs_read_block_bitmap(current_fs);
80                 if (retval) {
81                         com_err(device, retval, "while reading block bitmap");
82                         goto errout;
83                 }
84         }
85         root = cwd = EXT2_ROOT_INO;
86         return;
87
88 errout:
89         retval = ext2fs_close(current_fs);
90         if (retval)
91                 com_err(device, retval, "while trying to close filesystem");
92         current_fs = NULL;
93 }
94
95 void do_open_filesys(int argc, char **argv)
96 {
97         const char *usage = "Usage: open [-s superblock] [-b blocksize] [-c] [-w] <device>";
98         int     c, err;
99         int     catastrophic = 0;
100         blk_t   superblock = 0;
101         blk_t   blocksize = 0;
102         int open_flags = 0;
103         
104         optind = 0;
105 #ifdef HAVE_OPTRESET
106         optreset = 1;           /* Makes BSD getopt happy */
107 #endif
108         while ((c = getopt (argc, argv, "iwfcb:s:")) != EOF) {
109                 switch (c) {
110                 case 'i':
111                         open_flags |= EXT2_FLAG_IMAGE_FILE;
112                         break;
113                 case 'w':
114                         open_flags |= EXT2_FLAG_RW;
115                         break;
116                 case 'f':
117                         open_flags |= EXT2_FLAG_FORCE;
118                         break;
119                 case 'c':
120                         catastrophic = 1;
121                         break;
122                 case 'b':
123                         blocksize = parse_ulong(optarg, argv[0],
124                                                 "block size", &err);
125                         if (err)
126                                 return;
127                         break;
128                 case 's':
129                         superblock = parse_ulong(optarg, argv[0],
130                                                  "superblock number", &err);
131                         if (err)
132                                 return;
133                         break;
134                 default:
135                         com_err(argv[0], 0, usage);
136                         return;
137                 }
138         }
139         if (optind != argc-1) {
140                 com_err(argv[0], 0, usage);
141                 return;
142         }
143         if (check_fs_not_open(argv[0]))
144                 return;
145         open_filesystem(argv[optind], open_flags,
146                         superblock, blocksize, catastrophic);
147 }
148
149 void do_lcd(int argc, char **argv)
150 {
151         if (common_args_process(argc, argv, 2, 2, "lcd",
152                                 "<native dir>", 0))
153                 return;
154
155         if (chdir(argv[1]) == -1) {
156                 com_err(argv[0], errno,
157                         "while trying to change native directory to %s",
158                         argv[1]);
159                 return;
160         }
161 }
162
163 static void close_filesystem(NOARGS)
164 {
165         int     retval;
166         
167         if (current_fs->flags & EXT2_FLAG_IB_DIRTY) {
168                 retval = ext2fs_write_inode_bitmap(current_fs);
169                 if (retval)
170                         com_err("ext2fs_write_inode_bitmap", retval, "");
171         }
172         if (current_fs->flags & EXT2_FLAG_BB_DIRTY) {
173                 retval = ext2fs_write_block_bitmap(current_fs);
174                 if (retval)
175                         com_err("ext2fs_write_block_bitmap", retval, "");
176         }
177         retval = ext2fs_close(current_fs);
178         if (retval)
179                 com_err("ext2fs_close", retval, "");
180         current_fs = NULL;
181         return;
182 }
183
184 void do_close_filesys(int argc, char **argv)
185 {
186         if (common_args_process(argc, argv, 1, 1, "close_filesys", "", 0))
187                 return;
188         close_filesystem();
189 }
190
191 void do_init_filesys(int argc, char **argv)
192 {
193         struct ext2_super_block param;
194         errcode_t       retval;
195         int             err;
196         
197         if (common_args_process(argc, argv, 3, 3, "initialize",
198                                 "<device> <blocksize>", CHECK_FS_NOTOPEN))
199                 return;
200
201         memset(&param, 0, sizeof(struct ext2_super_block));
202         param.s_blocks_count = parse_ulong(argv[2], argv[0],
203                                            "blocks count", &err);
204         if (err)
205                 return;
206         retval = ext2fs_initialize(argv[1], 0, &param,
207                                    unix_io_manager, &current_fs);
208         if (retval) {
209                 com_err(argv[1], retval, "while initializing filesystem");
210                 current_fs = NULL;
211                 return;
212         }
213         root = cwd = EXT2_ROOT_INO;
214         return;
215 }
216
217 static void print_features(struct ext2_super_block * s, FILE *f)
218 {
219         int     i, j, printed=0;
220         __u32   *mask = &s->s_feature_compat, m;
221
222         fputs("Filesystem features:", f);
223         for (i=0; i <3; i++,mask++) {
224                 for (j=0,m=1; j < 32; j++, m<<=1) {
225                         if (*mask & m) {
226                                 fprintf(f, " %s", e2p_feature2string(i, m));
227                                 printed++;
228                         }
229                 }
230         }
231         if (printed == 0)
232                 fputs("(none)", f);
233         fputs("\n", f);
234 }
235
236 void do_show_super_stats(int argc, char *argv[])
237 {
238         int     i;
239         FILE    *out;
240         struct ext2_group_desc *gdp;
241         int     c, header_only = 0;
242         const char *usage = "Usage: show_super [-h]";
243
244         optind = 0;
245 #ifdef HAVE_OPTRESET
246         optreset = 1;           /* Makes BSD getopt happy */
247 #endif
248         while ((c = getopt (argc, argv, "h")) != EOF) {
249                 switch (c) {
250                 case 'h':
251                         header_only++;
252                         break;
253                 default:
254                         com_err(argv[0], 0, usage);
255                         return;
256                 }
257         }
258         if (optind != argc) {
259                 com_err(argv[0], 0, usage);
260                 return;
261         }
262         if (check_fs_open(argv[0]))
263                 return;
264         out = open_pager();
265
266         list_super2(current_fs->super, out);
267
268         if (header_only) {
269                 close_pager(out);
270                 return;
271         }
272         
273         gdp = &current_fs->group_desc[0];
274         for (i = 0; i < current_fs->group_desc_count; i++, gdp++)
275                 fprintf(out, " Group %2d: block bitmap at %d, "
276                         "inode bitmap at %d, "
277                         "inode table at %d\n"
278                         "           %d free %s, "
279                         "%d free %s, "
280                         "%d used %s\n",
281                         i, gdp->bg_block_bitmap,
282                         gdp->bg_inode_bitmap, gdp->bg_inode_table,
283                         gdp->bg_free_blocks_count,
284                         gdp->bg_free_blocks_count != 1 ? "blocks" : "block",
285                         gdp->bg_free_inodes_count,
286                         gdp->bg_free_inodes_count != 1 ? "inodes" : "inode",
287                         gdp->bg_used_dirs_count,
288                         gdp->bg_used_dirs_count != 1 ? "directories"
289                                 : "directory");
290         close_pager(out);
291 }
292
293 void do_dirty_filesys(int argc, char **argv)
294 {
295         if (check_fs_open(argv[0]))
296                 return;
297         if (check_fs_read_write(argv[0]))
298                 return;
299
300         if (argv[1] && !strcmp(argv[1], "-clean"))
301                 current_fs->super->s_state |= EXT2_VALID_FS;
302         else
303                 current_fs->super->s_state &= ~EXT2_VALID_FS;
304         ext2fs_mark_super_dirty(current_fs);
305 }
306
307 struct list_blocks_struct {
308         FILE            *f;
309         e2_blkcnt_t     total;
310         blk_t           first_block, last_block;
311         e2_blkcnt_t     first_bcnt, last_bcnt;
312         e2_blkcnt_t     first;
313 };
314
315 static void finish_range(struct list_blocks_struct *lb)
316 {
317         if (lb->first_block == 0)
318                 return;
319         if (lb->first)
320                 lb->first = 0;
321         else
322                 fprintf(lb->f, ", ");
323         if (lb->first_block == lb->last_block)
324                 fprintf(lb->f, "(%lld):%d", lb->first_bcnt, lb->first_block);
325         else
326                 fprintf(lb->f, "(%lld-%lld):%d-%d", lb->first_bcnt,
327                         lb->last_bcnt, lb->first_block, lb->last_block);
328         lb->first_block = 0;
329 }
330
331 static int list_blocks_proc(ext2_filsys fs, blk_t *blocknr,
332                             e2_blkcnt_t blockcnt, blk_t ref_block,
333                             int ref_offset, void *private)
334 {
335         struct list_blocks_struct *lb = (struct list_blocks_struct *) private;
336
337         lb->total++;
338         if (blockcnt >= 0) {
339                 /*
340                  * See if we can add on to the existing range (if it exists)
341                  */
342                 if (lb->first_block &&
343                     (lb->last_block+1 == *blocknr) &&
344                     (lb->last_bcnt+1 == blockcnt)) {
345                         lb->last_block = *blocknr;
346                         lb->last_bcnt = blockcnt;
347                         return 0;
348                 }
349                 /*
350                  * Start a new range.
351                  */
352                 finish_range(lb);
353                 lb->first_block = lb->last_block = *blocknr;
354                 lb->first_bcnt = lb->last_bcnt = blockcnt;
355                 return 0;
356         }
357         /*
358          * Not a normal block.  Always force a new range.
359          */
360         finish_range(lb);
361         if (lb->first)
362                 lb->first = 0;
363         else
364                 fprintf(lb->f, ", ");
365         if (blockcnt == -1)
366                 fprintf(lb->f, "(IND):%d", *blocknr);
367         else if (blockcnt == -2)
368                 fprintf(lb->f, "(DIND):%d", *blocknr);
369         else if (blockcnt == -3)
370                 fprintf(lb->f, "(TIND):%d", *blocknr);
371         return 0;
372 }
373
374
375 static void dump_blocks(FILE *f, const char *prefix, ext2_ino_t inode)
376 {
377         struct list_blocks_struct lb;
378
379         fprintf(f, "%sBLOCKS:\n%s", prefix, prefix);
380         lb.total = 0;
381         lb.first_block = 0;
382         lb.f = f;
383         lb.first = 1;
384         ext2fs_block_iterate2(current_fs, inode, 0, NULL,
385                              list_blocks_proc, (void *)&lb);
386         finish_range(&lb);
387         if (lb.total)
388                 fprintf(f, "\n%sTOTAL: %lld\n", prefix, lb.total);
389         fprintf(f,"\n");
390 }
391
392
393 void internal_dump_inode(FILE *out, const char *prefix,
394                          ext2_ino_t inode_num, struct ext2_inode *inode,
395                          int do_dump_blocks)
396 {
397         const char *i_type;
398         char frag, fsize;
399         int os = current_fs->super->s_creator_os;
400         
401         if (LINUX_S_ISDIR(inode->i_mode)) i_type = "directory";
402         else if (LINUX_S_ISREG(inode->i_mode)) i_type = "regular";
403         else if (LINUX_S_ISLNK(inode->i_mode)) i_type = "symlink";
404         else if (LINUX_S_ISBLK(inode->i_mode)) i_type = "block special";
405         else if (LINUX_S_ISCHR(inode->i_mode)) i_type = "character special";
406         else if (LINUX_S_ISFIFO(inode->i_mode)) i_type = "FIFO";
407         else if (LINUX_S_ISSOCK(inode->i_mode)) i_type = "socket";
408         else i_type = "bad type";
409         fprintf(out, "%sInode: %u   Type: %s    ", prefix, inode_num, i_type);
410         fprintf(out, "%sMode:  %04o   Flags: 0x%x   Generation: %u\n",
411                 prefix, 
412                 inode->i_mode & 0777, inode->i_flags, inode->i_generation);
413         fprintf(out, "%sUser: %5d   Group: %5d   Size: ",
414                 prefix, inode->i_uid, inode->i_gid);
415         if (LINUX_S_ISREG(inode->i_mode)) {
416                 __u64 i_size = (inode->i_size |
417                                 ((unsigned long long)inode->i_size_high << 32));
418
419                 fprintf(out, "%lld\n", i_size);
420         } else
421                 fprintf(out, "%d\n", inode->i_size);
422         if (current_fs->super->s_creator_os == EXT2_OS_HURD)
423                 fprintf(out,
424                         "%sFile ACL: %d    Directory ACL: %d Translator: %d\n",
425                         prefix,
426                         inode->i_file_acl, LINUX_S_ISDIR(inode->i_mode) ? inode->i_dir_acl : 0,
427                         inode->osd1.hurd1.h_i_translator);
428         else
429                 fprintf(out, "%sFile ACL: %d    Directory ACL: %d\n",
430                         prefix,
431                         inode->i_file_acl, LINUX_S_ISDIR(inode->i_mode) ? inode->i_dir_acl : 0);
432         fprintf(out, "%sLinks: %d   Blockcount: %d\n", 
433                 prefix, inode->i_links_count, inode->i_blocks);
434         switch (os) {
435             case EXT2_OS_LINUX:
436                 frag = inode->osd2.linux2.l_i_frag;
437                 fsize = inode->osd2.linux2.l_i_fsize;
438                 break;
439             case EXT2_OS_HURD:
440                 frag = inode->osd2.hurd2.h_i_frag;
441                 fsize = inode->osd2.hurd2.h_i_fsize;
442                 break;
443             case EXT2_OS_MASIX:
444                 frag = inode->osd2.masix2.m_i_frag;
445                 fsize = inode->osd2.masix2.m_i_fsize;
446                 break;
447             default:
448                 frag = fsize = 0;
449         }
450         fprintf(out, "%sFragment:  Address: %d    Number: %d    Size: %d\n",
451                 prefix, inode->i_faddr, frag, fsize);
452         fprintf(out, "%sctime: 0x%08x -- %s", prefix, inode->i_ctime,
453                 time_to_string(inode->i_ctime));
454         fprintf(out, "%satime: 0x%08x -- %s", prefix, inode->i_atime,
455                 time_to_string(inode->i_atime));
456         fprintf(out, "%smtime: 0x%08x -- %s", prefix, inode->i_mtime,
457                 time_to_string(inode->i_mtime));
458         if (inode->i_dtime) 
459           fprintf(out, "%sdtime: 0x%08x -- %s", prefix, inode->i_dtime,
460                   time_to_string(inode->i_dtime));
461         if (LINUX_S_ISLNK(inode->i_mode) && inode->i_blocks == 0)
462                 fprintf(out, "%sFast_link_dest: %.*s\n", prefix,
463                         (int) inode->i_size, (char *)inode->i_block);
464         else if (do_dump_blocks)
465                 dump_blocks(out, prefix, inode_num);
466 }
467
468 static void dump_inode(ext2_ino_t inode_num, struct ext2_inode inode)
469 {
470         FILE    *out;
471         
472         out = open_pager();
473         internal_dump_inode(out, "", inode_num, &inode, 1);
474         close_pager(out);
475 }
476
477 void do_stat(int argc, char *argv[])
478 {
479         ext2_ino_t      inode;
480         struct ext2_inode inode_buf;
481
482         if (common_inode_args_process(argc, argv, &inode, 0))
483                 return;
484
485         if (debugfs_read_inode(inode, &inode_buf, argv[0]))
486                 return;
487
488         dump_inode(inode,inode_buf);
489         return;
490 }
491
492 void do_chroot(int argc, char *argv[])
493 {
494         ext2_ino_t inode;
495         int retval;
496
497         if (common_inode_args_process(argc, argv, &inode, 0))
498                 return;
499
500         retval = ext2fs_check_directory(current_fs, inode);
501         if (retval)  {
502                 com_err(argv[1], retval, "");
503                 return;
504         }
505         root = inode;
506 }
507
508 void do_clri(int argc, char *argv[])
509 {
510         ext2_ino_t inode;
511         struct ext2_inode inode_buf;
512
513         if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
514                 return;
515
516         if (debugfs_read_inode(inode, &inode_buf, argv[0]))
517                 return;
518         memset(&inode_buf, 0, sizeof(inode_buf));
519         if (debugfs_write_inode(inode, &inode_buf, argv[0]))
520                 return;
521 }
522
523 void do_freei(int argc, char *argv[])
524 {
525         ext2_ino_t inode;
526
527         if (common_inode_args_process(argc, argv, &inode,
528                                       CHECK_FS_RW | CHECK_FS_BITMAPS))
529                 return;
530
531         if (!ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
532                 com_err(argv[0], 0, "Warning: inode already clear");
533         ext2fs_unmark_inode_bitmap(current_fs->inode_map,inode);
534         ext2fs_mark_ib_dirty(current_fs);
535 }
536
537 void do_seti(int argc, char *argv[])
538 {
539         ext2_ino_t inode;
540
541         if (common_inode_args_process(argc, argv, &inode,
542                                       CHECK_FS_RW | CHECK_FS_BITMAPS))
543                 return;
544
545         if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
546                 com_err(argv[0], 0, "Warning: inode already set");
547         ext2fs_mark_inode_bitmap(current_fs->inode_map,inode);
548         ext2fs_mark_ib_dirty(current_fs);
549 }
550
551 void do_testi(int argc, char *argv[])
552 {
553         ext2_ino_t inode;
554
555         if (common_inode_args_process(argc, argv, &inode, CHECK_FS_BITMAPS))
556                 return;
557
558         if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
559                 printf("Inode %u is marked in use\n", inode);
560         else
561                 printf("Inode %u is not in use\n", inode);
562 }
563
564 void do_freeb(int argc, char *argv[])
565 {
566         blk_t block;
567         int count = 1;
568
569         if (common_block_args_process(argc, argv, &block, &count))
570                 return;
571         if (check_fs_read_write(argv[0]))
572                 return;
573         while (count-- > 0) {
574                 if (!ext2fs_test_block_bitmap(current_fs->block_map,block))
575                         com_err(argv[0], 0, "Warning: block %d already clear",
576                                 block);
577                 ext2fs_unmark_block_bitmap(current_fs->block_map,block);
578                 block++;
579         }
580         ext2fs_mark_bb_dirty(current_fs);
581 }
582
583 void do_setb(int argc, char *argv[])
584 {
585         blk_t block;
586         int count = 1;
587
588         if (common_block_args_process(argc, argv, &block, &count))
589                 return;
590         if (check_fs_read_write(argv[0]))
591                 return;
592         while (count-- > 0) {
593                 if (ext2fs_test_block_bitmap(current_fs->block_map,block))
594                         com_err(argv[0], 0, "Warning: block %d already set",
595                                 block);
596                 ext2fs_mark_block_bitmap(current_fs->block_map,block);
597                 block++;
598         }
599         ext2fs_mark_bb_dirty(current_fs);
600 }
601
602 void do_testb(int argc, char *argv[])
603 {
604         blk_t block;
605         int count = 1;
606
607         if (common_block_args_process(argc, argv, &block, &count))
608                 return;
609         while (count-- > 0) {
610                 if (ext2fs_test_block_bitmap(current_fs->block_map,block))
611                         printf("Block %d marked in use\n", block);
612                 else
613                         printf("Block %d not in use\n", block);
614                 block++;
615         }
616 }
617
618 static void modify_u8(char *com, const char *prompt,
619                       const char *format, __u8 *val)
620 {
621         char buf[200];
622         unsigned long v;
623         char *tmp;
624
625         sprintf(buf, format, *val);
626         printf("%30s    [%s] ", prompt, buf);
627         fgets(buf, sizeof(buf), stdin);
628         if (buf[strlen (buf) - 1] == '\n')
629                 buf[strlen (buf) - 1] = '\0';
630         if (!buf[0])
631                 return;
632         v = strtoul(buf, &tmp, 0);
633         if (*tmp)
634                 com_err(com, 0, "Bad value - %s", buf);
635         else
636                 *val = v;
637 }
638
639 static void modify_u16(char *com, const char *prompt,
640                        const char *format, __u16 *val)
641 {
642         char buf[200];
643         unsigned long v;
644         char *tmp;
645
646         sprintf(buf, format, *val);
647         printf("%30s    [%s] ", prompt, buf);
648         fgets(buf, sizeof(buf), stdin);
649         if (buf[strlen (buf) - 1] == '\n')
650                 buf[strlen (buf) - 1] = '\0';
651         if (!buf[0])
652                 return;
653         v = strtoul(buf, &tmp, 0);
654         if (*tmp)
655                 com_err(com, 0, "Bad value - %s", buf);
656         else
657                 *val = v;
658 }
659
660 static void modify_u32(char *com, const char *prompt,
661                        const char *format, __u32 *val)
662 {
663         char buf[200];
664         unsigned long v;
665         char *tmp;
666
667         sprintf(buf, format, *val);
668         printf("%30s    [%s] ", prompt, buf);
669         fgets(buf, sizeof(buf), stdin);
670         if (buf[strlen (buf) - 1] == '\n')
671                 buf[strlen (buf) - 1] = '\0';
672         if (!buf[0])
673                 return;
674         v = strtoul(buf, &tmp, 0);
675         if (*tmp)
676                 com_err(com, 0, "Bad value - %s", buf);
677         else
678                 *val = v;
679 }
680
681
682 void do_modify_inode(int argc, char *argv[])
683 {
684         struct ext2_inode inode;
685         ext2_ino_t      inode_num;
686         int             i;
687         unsigned char   *frag, *fsize;
688         char            buf[80];
689         int             os;
690         const char      *hex_format = "0x%x";
691         const char      *octal_format = "0%o";
692         const char      *decimal_format = "%d";
693         
694         if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
695                 return;
696
697         os = current_fs->super->s_creator_os;
698
699         if (debugfs_read_inode(inode_num, &inode, argv[1]))
700                 return;
701         
702         modify_u16(argv[0], "Mode", octal_format, &inode.i_mode);
703         modify_u16(argv[0], "User ID", decimal_format, &inode.i_uid);
704         modify_u16(argv[0], "Group ID", decimal_format, &inode.i_gid);
705         modify_u32(argv[0], "Size", decimal_format, &inode.i_size);
706         modify_u32(argv[0], "Creation time", decimal_format, &inode.i_ctime);
707         modify_u32(argv[0], "Modification time", decimal_format, &inode.i_mtime);
708         modify_u32(argv[0], "Access time", decimal_format, &inode.i_atime);
709         modify_u32(argv[0], "Deletion time", decimal_format, &inode.i_dtime);
710         modify_u16(argv[0], "Link count", decimal_format, &inode.i_links_count);
711         modify_u32(argv[0], "Block count", decimal_format, &inode.i_blocks);
712         modify_u32(argv[0], "File flags", hex_format, &inode.i_flags);
713         modify_u32(argv[0], "Generation", hex_format, &inode.i_generation);
714 #if 0
715         modify_u32(argv[0], "Reserved1", decimal_format, &inode.i_reserved1);
716 #endif
717         modify_u32(argv[0], "File acl", decimal_format, &inode.i_file_acl);
718         if (LINUX_S_ISDIR(inode.i_mode))
719                 modify_u32(argv[0], "Directory acl", decimal_format, &inode.i_dir_acl);
720         else
721                 modify_u32(argv[0], "High 32bits of size", decimal_format, &inode.i_size_high);
722
723         if (current_fs->super->s_creator_os == EXT2_OS_HURD)
724                 modify_u32(argv[0], "Translator Block",
725                             decimal_format, &inode.osd1.hurd1.h_i_translator);
726         
727         modify_u32(argv[0], "Fragment address", decimal_format, &inode.i_faddr);
728         switch (os) {
729             case EXT2_OS_LINUX:
730                 frag = &inode.osd2.linux2.l_i_frag;
731                 fsize = &inode.osd2.linux2.l_i_fsize;
732                 break;
733             case EXT2_OS_HURD:
734                 frag = &inode.osd2.hurd2.h_i_frag;
735                 fsize = &inode.osd2.hurd2.h_i_fsize;
736                 break;
737             case EXT2_OS_MASIX:
738                 frag = &inode.osd2.masix2.m_i_frag;
739                 fsize = &inode.osd2.masix2.m_i_fsize;
740                 break;
741             default:
742                 frag = fsize = 0;
743         }
744         if (frag)
745                 modify_u8(argv[0], "Fragment number", decimal_format, frag);
746         if (fsize)
747                 modify_u8(argv[0], "Fragment size", decimal_format, fsize);
748
749         for (i=0;  i < EXT2_NDIR_BLOCKS; i++) {
750                 sprintf(buf, "Direct Block #%d", i);
751                 modify_u32(argv[0], buf, decimal_format, &inode.i_block[i]);
752         }
753         modify_u32(argv[0], "Indirect Block", decimal_format,
754                     &inode.i_block[EXT2_IND_BLOCK]);    
755         modify_u32(argv[0], "Double Indirect Block", decimal_format,
756                     &inode.i_block[EXT2_DIND_BLOCK]);
757         modify_u32(argv[0], "Triple Indirect Block", decimal_format,
758                     &inode.i_block[EXT2_TIND_BLOCK]);
759         if (debugfs_write_inode(inode_num, &inode, argv[1]))
760                 return;
761 }
762
763 void do_change_working_dir(int argc, char *argv[])
764 {
765         ext2_ino_t      inode;
766         int             retval;
767         
768         if (common_inode_args_process(argc, argv, &inode, 0))
769                 return;
770
771         retval = ext2fs_check_directory(current_fs, inode);
772         if (retval) {
773                 com_err(argv[1], retval, "");
774                 return;
775         }
776         cwd = inode;
777         return;
778 }
779
780 void do_print_working_directory(int argc, char *argv[])
781 {
782         int     retval;
783         char    *pathname = NULL;
784         
785         if (common_args_process(argc, argv, 1, 1,
786                                 "print_working_directory", "", 0))
787                 return;
788
789         retval = ext2fs_get_pathname(current_fs, cwd, 0, &pathname);
790         if (retval) {
791                 com_err(argv[0], retval,
792                         "while trying to get pathname of cwd");
793         }
794         printf("[pwd]   INODE: %6u  PATH: %s\n", cwd, pathname);
795         free(pathname);
796         retval = ext2fs_get_pathname(current_fs, root, 0, &pathname);
797         if (retval) {
798                 com_err(argv[0], retval,
799                         "while trying to get pathname of root");
800         }
801         printf("[root]  INODE: %6u  PATH: %s\n", root, pathname);
802         free(pathname);
803         return;
804 }
805
806 static void make_link(char *sourcename, char *destname)
807 {
808         ext2_ino_t      inode;
809         int             retval;
810         ext2_ino_t      dir;
811         char            *dest, *cp, *basename;
812
813         /*
814          * Get the source inode
815          */
816         inode = string_to_inode(sourcename);
817         if (!inode)
818                 return;
819         basename = strrchr(sourcename, '/');
820         if (basename)
821                 basename++;
822         else
823                 basename = sourcename;
824         /*
825          * Figure out the destination.  First see if it exists and is
826          * a directory.  
827          */
828         if (! (retval=ext2fs_namei(current_fs, root, cwd, destname, &dir)))
829                 dest = basename;
830         else {
831                 /*
832                  * OK, it doesn't exist.  See if it is
833                  * '<dir>/basename' or 'basename'
834                  */
835                 cp = strrchr(destname, '/');
836                 if (cp) {
837                         *cp = 0;
838                         dir = string_to_inode(destname);
839                         if (!dir)
840                                 return;
841                         dest = cp+1;
842                 } else {
843                         dir = cwd;
844                         dest = destname;
845                 }
846         }
847         
848         retval = ext2fs_link(current_fs, dir, dest, inode, 0);
849         if (retval)
850                 com_err("make_link", retval, "");
851         return;
852 }
853
854
855 void do_link(int argc, char *argv[])
856 {
857         if (common_args_process(argc, argv, 3, 3, "link",
858                                 "<source file> <dest_name>", CHECK_FS_RW))
859                 return;
860
861         make_link(argv[1], argv[2]);
862 }
863
864 static int mark_blocks_proc(ext2_filsys fs, blk_t *blocknr,
865                             int blockcnt, void *private)
866 {
867         blk_t   block;
868
869         block = *blocknr;
870         ext2fs_block_alloc_stats(fs, block, +1);
871         return 0;
872 }
873
874 void do_undel(int argc, char *argv[])
875 {
876         ext2_ino_t      ino;
877         struct ext2_inode inode;
878
879         if (common_args_process(argc, argv, 3, 3, "undelete",
880                                 "<inode_num> <dest_name>",
881                                 CHECK_FS_RW | CHECK_FS_BITMAPS))
882                 return;
883
884         ino = string_to_inode(argv[1]);
885         if (!ino)
886                 return;
887
888         if (debugfs_read_inode(ino, &inode, argv[1]))
889                 return;
890
891         if (ext2fs_test_inode_bitmap(current_fs->inode_map, ino)) {
892                 com_err(argv[1], 0, "Inode is not marked as deleted");
893                 return;
894         }
895
896         /*
897          * XXX this function doesn't handle changing the links count on the
898          * parent directory when undeleting a directory.  
899          */
900         inode.i_links_count = LINUX_S_ISDIR(inode.i_mode) ? 2 : 1;
901         inode.i_dtime = 0;
902
903         if (debugfs_write_inode(ino, &inode, argv[0]))
904                 return;
905
906         ext2fs_block_iterate(current_fs, ino, 0, NULL,
907                              mark_blocks_proc, NULL);
908
909         ext2fs_inode_alloc_stats(current_fs, ino, +1);
910
911         make_link(argv[1], argv[2]);
912 }
913
914 static void unlink_file_by_name(char *filename)
915 {
916         int             retval;
917         ext2_ino_t      dir;
918         char            *basename;
919         
920         basename = strrchr(filename, '/');
921         if (basename) {
922                 *basename++ = '\0';
923                 dir = string_to_inode(filename);
924                 if (!dir)
925                         return;
926         } else {
927                 dir = cwd;
928                 basename = filename;
929         }
930         retval = ext2fs_unlink(current_fs, dir, basename, 0, 0);
931         if (retval)
932                 com_err("unlink_file_by_name", retval, "");
933         return;
934 }
935
936 void do_unlink(int argc, char *argv[])
937 {
938         if (common_args_process(argc, argv, 2, 2, "link",
939                                 "<pathname>", CHECK_FS_RW))
940                 return;
941
942         unlink_file_by_name(argv[1]);
943 }
944
945 void do_find_free_block(int argc, char *argv[])
946 {
947         blk_t   free_blk, goal;
948         int             count;
949         errcode_t       retval;
950         char            *tmp;
951         
952         if ((argc > 3) || (argc==2 && *argv[1] == '?')) {
953                 com_err(argv[0], 0, "Usage: find_free_block [count [goal]]");
954                 return;
955         }
956         if (check_fs_open(argv[0]))
957                 return;
958
959         if (argc > 1) {
960                 count = strtol(argv[1],&tmp,0);
961                 if (*tmp) {
962                         com_err(argv[0], 0, "Bad count - %s", argv[1]);
963                         return;
964                 }
965         } else
966                 count = 1;
967
968         if (argc > 2) {
969                 goal = strtol(argv[2], &tmp, 0);
970                 if (*tmp) {
971                         com_err(argv[0], 0, "Bad goal - %s", argv[1]);
972                         return;
973                 }
974         }
975         else
976                 goal = current_fs->super->s_first_data_block;
977
978         printf("Free blocks found: ");
979         free_blk = goal - 1;    
980         while (count-- > 0) {
981                 retval = ext2fs_new_block(current_fs, free_blk + 1, 0,
982                                           &free_blk);
983                 if (retval) {
984                         com_err("ext2fs_new_block", retval, "");
985                         return;
986                 } else
987                         printf("%d ", free_blk);
988         }
989         printf("\n");
990 }
991
992 void do_find_free_inode(int argc, char *argv[])
993 {
994         ext2_ino_t      free_inode, dir;
995         int             mode;
996         int             retval;
997         char            *tmp;
998         
999         if (argc > 3 || (argc>1 && *argv[1] == '?')) {
1000                 com_err(argv[0], 0, "Usage: find_free_inode [dir] [mode]");
1001                 return;
1002         }
1003         if (check_fs_open(argv[0]))
1004                 return;
1005
1006         if (argc > 1) {
1007                 dir = strtol(argv[1], &tmp, 0);
1008                 if (*tmp) {
1009                         com_err(argv[0], 0, "Bad dir - %s", argv[1]);
1010                         return;
1011                 }
1012         }
1013         else
1014                 dir = root;
1015         if (argc > 2) {
1016                 mode = strtol(argv[2], &tmp, 0);
1017                 if (*tmp) {
1018                         com_err(argv[0], 0, "Bad mode - %s", argv[2]);
1019                         return;
1020                 }
1021         } else
1022                 mode = 010755;
1023
1024         retval = ext2fs_new_inode(current_fs, dir, mode, 0, &free_inode);
1025         if (retval)
1026                 com_err("ext2fs_new_inode", retval, "");
1027         else
1028                 printf("Free inode found: %u\n", free_inode);
1029 }
1030
1031 static errcode_t copy_file(int fd, ext2_ino_t newfile)
1032 {
1033         ext2_file_t     e2_file;
1034         errcode_t       retval;
1035         int             got;
1036         unsigned int    written;
1037         char            buf[8192];
1038         char            *ptr;
1039
1040         retval = ext2fs_file_open(current_fs, newfile,
1041                                   EXT2_FILE_WRITE, &e2_file);
1042         if (retval)
1043                 return retval;
1044
1045         while (1) {
1046                 got = read(fd, buf, sizeof(buf));
1047                 if (got == 0)
1048                         break;
1049                 if (got < 0) {
1050                         retval = errno;
1051                         goto fail;
1052                 }
1053                 ptr = buf;
1054                 while (got > 0) {
1055                         retval = ext2fs_file_write(e2_file, ptr,
1056                                                    got, &written);
1057                         if (retval)
1058                                 goto fail;
1059
1060                         got -= written;
1061                         ptr += written;
1062                 }
1063         }
1064         retval = ext2fs_file_close(e2_file);
1065         return retval;
1066
1067 fail:
1068         (void) ext2fs_file_close(e2_file);
1069         return retval;
1070 }
1071
1072
1073 void do_write(int argc, char *argv[])
1074 {
1075         int             fd;
1076         struct stat     statbuf;
1077         ext2_ino_t      newfile;
1078         errcode_t       retval;
1079         struct ext2_inode inode;
1080
1081         if (common_args_process(argc, argv, 3, 3, "write",
1082                                 "<native file> <new file>", CHECK_FS_RW))
1083                 return;
1084
1085         fd = open(argv[1], O_RDONLY);
1086         if (fd < 0) {
1087                 com_err(argv[1], errno, "");
1088                 return;
1089         }
1090         if (fstat(fd, &statbuf) < 0) {
1091                 com_err(argv[1], errno, "");
1092                 close(fd);
1093                 return;
1094         }
1095
1096         retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
1097         if (retval) {
1098                 com_err(argv[0], retval, "");
1099                 close(fd);
1100                 return;
1101         }
1102         printf("Allocated inode: %u\n", newfile);
1103         retval = ext2fs_link(current_fs, cwd, argv[2], newfile,
1104                              EXT2_FT_REG_FILE);
1105         if (retval) {
1106                 com_err(argv[2], retval, "");
1107                 close(fd);
1108                 return;
1109         }
1110         if (ext2fs_test_inode_bitmap(current_fs->inode_map,newfile))
1111                 com_err(argv[0], 0, "Warning: inode already set");
1112         ext2fs_inode_alloc_stats(current_fs, newfile, +1);
1113         memset(&inode, 0, sizeof(inode));
1114         inode.i_mode = statbuf.st_mode;
1115         inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
1116         inode.i_links_count = 1;
1117         inode.i_size = statbuf.st_size;
1118         if (debugfs_write_inode(newfile, &inode, argv[0])) {
1119                 close(fd);
1120                 return;
1121         }
1122         if (LINUX_S_ISREG(inode.i_mode)) {
1123                 retval = copy_file(fd, newfile);
1124                 if (retval)
1125                         com_err("copy_file", retval, "");
1126         }
1127         close(fd);
1128 }
1129
1130 void do_mknod(int argc, char *argv[])
1131 {
1132         unsigned long   mode, major, minor, nr;
1133         ext2_ino_t      newfile;
1134         errcode_t       retval;
1135         struct ext2_inode inode;
1136         int             filetype;
1137
1138         if (check_fs_open(argv[0]))
1139                 return;
1140         if (argc < 3 || argv[2][1]) {
1141         usage:
1142                 com_err(argv[0], 0, "Usage: mknod <name> [p| [c|b] <major> <minor>]");
1143                 return;
1144         }
1145         mode = minor = major = 0;
1146         switch (argv[2][0]) {
1147                 case 'p':
1148                         mode = LINUX_S_IFIFO;
1149                         filetype = EXT2_FT_FIFO;
1150                         nr = 3;
1151                         break;
1152                 case 'c':
1153                         mode = LINUX_S_IFCHR;
1154                         filetype = EXT2_FT_CHRDEV;
1155                         nr = 5;
1156                         break;
1157                 case 'b':
1158                         mode = LINUX_S_IFBLK;
1159                         filetype = EXT2_FT_BLKDEV;
1160                         nr = 5;
1161                         break;
1162                 default:
1163                         filetype = 0;
1164                         nr = 0;
1165         }
1166         if (nr == 5) {
1167                 major = strtoul(argv[3], argv+3, 0);
1168                 minor = strtoul(argv[4], argv+4, 0);
1169                 if (major > 255 || minor > 255 || argv[3][0] || argv[4][0])
1170                         nr = 0;
1171         }
1172         if (argc != nr)
1173                 goto usage;
1174         if (check_fs_read_write(argv[0]))
1175                 return;
1176         retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
1177         if (retval) {
1178                 com_err(argv[0], retval, "");
1179                 return;
1180         }
1181         printf("Allocated inode: %u\n", newfile);
1182         retval = ext2fs_link(current_fs, cwd, argv[1], newfile, filetype);
1183         if (retval) {
1184                 if (retval == EXT2_ET_DIR_NO_SPACE) {
1185                         retval = ext2fs_expand_dir(current_fs, cwd);
1186                         if (!retval)
1187                                 retval = ext2fs_link(current_fs, cwd,
1188                                                      argv[1], newfile,
1189                                                      filetype);
1190                 }
1191                 if (retval) {
1192                         com_err(argv[1], retval, "");
1193                         return;
1194                 }
1195         }
1196         if (ext2fs_test_inode_bitmap(current_fs->inode_map,newfile))
1197                 com_err(argv[0], 0, "Warning: inode already set");
1198         ext2fs_mark_inode_bitmap(current_fs->inode_map, newfile);
1199         ext2fs_mark_ib_dirty(current_fs);
1200         memset(&inode, 0, sizeof(inode));
1201         inode.i_mode = mode;
1202         inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
1203         inode.i_block[0] = major*256+minor;
1204         inode.i_links_count = 1;
1205         if (debugfs_write_inode(newfile, &inode, argv[0]))
1206                 return;
1207 }
1208
1209 void do_mkdir(int argc, char *argv[])
1210 {
1211         char    *cp;
1212         ext2_ino_t      parent;
1213         char    *name;
1214         errcode_t retval;
1215
1216         if (common_args_process(argc, argv, 2, 2, "mkdir",
1217                                 "<filename>", CHECK_FS_RW))
1218                 return;
1219
1220         cp = strrchr(argv[1], '/');
1221         if (cp) {
1222                 *cp = 0;
1223                 parent = string_to_inode(argv[1]);
1224                 if (!parent) {
1225                         com_err(argv[1], ENOENT, "");
1226                         return;
1227                 }
1228                 name = cp+1;
1229         } else {
1230                 parent = cwd;
1231                 name = argv[1];
1232         }
1233
1234
1235         retval = ext2fs_mkdir(current_fs, parent, 0, name);
1236         if (retval) {
1237                 com_err("ext2fs_mkdir", retval, "");
1238                 return;
1239         }
1240
1241 }
1242
1243 void do_rmdir(int argc, char *argv[])
1244 {
1245         printf("Unimplemented\n");
1246 }
1247
1248
1249 static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
1250                                int blockcnt, void *private)
1251 {
1252         blk_t   block;
1253
1254         block = *blocknr;
1255         ext2fs_block_alloc_stats(fs, block, -1);
1256         return 0;
1257 }
1258
1259 static void kill_file_by_inode(ext2_ino_t inode)
1260 {
1261         struct ext2_inode inode_buf;
1262
1263         if (debugfs_read_inode(inode, &inode_buf, 0))
1264                 return;
1265         inode_buf.i_dtime = time(NULL);
1266         if (debugfs_write_inode(inode, &inode_buf, 0))
1267                 return;
1268
1269         printf("Kill file by inode %u\n", inode);
1270         ext2fs_block_iterate(current_fs, inode, 0, NULL,
1271                              release_blocks_proc, NULL);
1272         printf("\n");
1273         ext2fs_inode_alloc_stats(current_fs, inode, -1);
1274 }
1275
1276
1277 void do_kill_file(int argc, char *argv[])
1278 {
1279         ext2_ino_t inode_num;
1280
1281         if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
1282                 return;
1283
1284         kill_file_by_inode(inode_num);
1285 }
1286
1287 void do_rm(int argc, char *argv[])
1288 {
1289         int retval;
1290         ext2_ino_t inode_num;
1291         struct ext2_inode inode;
1292
1293         if (common_args_process(argc, argv, 2, 2, "rm",
1294                                 "<filename>", CHECK_FS_RW))
1295                 return;
1296
1297         retval = ext2fs_namei(current_fs, root, cwd, argv[1], &inode_num);
1298         if (retval) {
1299                 com_err(argv[0], retval, "while trying to resolve filename");
1300                 return;
1301         }
1302
1303         if (debugfs_read_inode(inode_num, &inode, argv[0]))
1304                 return;
1305
1306         if (LINUX_S_ISDIR(inode.i_mode)) {
1307                 com_err(argv[0], 0, "file is a directory");
1308                 return;
1309         }
1310
1311         --inode.i_links_count;
1312         if (debugfs_write_inode(inode_num, &inode, argv[0]))
1313                 return;
1314
1315         unlink_file_by_name(argv[1]);
1316         if (inode.i_links_count == 0)
1317                 kill_file_by_inode(inode_num);
1318 }
1319
1320 void do_show_debugfs_params(int argc, char *argv[])
1321 {
1322         FILE *out = stdout;
1323
1324         if (current_fs)
1325                 fprintf(out, "Open mode: read-%s\n",
1326                         current_fs->flags & EXT2_FLAG_RW ? "write" : "only");
1327         fprintf(out, "Filesystem in use: %s\n",
1328                 current_fs ? current_fs->device_name : "--none--");
1329 }
1330
1331 void do_expand_dir(int argc, char *argv[])
1332 {
1333         ext2_ino_t inode;
1334         int retval;
1335
1336         if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
1337                 return;
1338
1339         retval = ext2fs_expand_dir(current_fs, inode);
1340         if (retval)
1341                 com_err("ext2fs_expand_dir", retval, "");
1342         return;
1343 }
1344
1345 void do_features(int argc, char *argv[])
1346 {
1347         int     i;
1348         
1349         if (check_fs_open(argv[0]))
1350                 return;
1351
1352         if ((argc != 1) && check_fs_read_write(argv[0]))
1353                 return;
1354         for (i=1; i < argc; i++) {
1355                 if (e2p_edit_feature(argv[i],
1356                                      &current_fs->super->s_feature_compat, 0))
1357                         com_err(argv[0], 0, "Unknown feature: %s\n",
1358                                 argv[i]);
1359                 else
1360                         ext2fs_mark_super_dirty(current_fs);
1361         }
1362         print_features(current_fs->super, stdout);
1363 }
1364
1365 void do_bmap(int argc, char *argv[])
1366 {
1367         ext2_ino_t      ino;
1368         blk_t           blk, pblk;
1369         int             err;
1370         errcode_t       errcode;
1371         
1372         if (common_args_process(argc, argv, 3, 3, argv[0],
1373                                 "<file> logical_blk", 0))
1374                 return;
1375
1376         ino = string_to_inode(argv[1]);
1377         blk = parse_ulong(argv[2], argv[0], "logical_block", &err);
1378
1379         errcode = ext2fs_bmap(current_fs, ino, 0, 0, 0, blk, &pblk);
1380         if (errcode) {
1381                 com_err("argv[0]", errcode,
1382                         "while mapping logical block %d\n", blk);
1383                 return;
1384         }
1385         printf("%d\n", pblk);
1386 }
1387
1388
1389 static int source_file(const char *cmd_file, int sci_idx)
1390 {
1391         FILE            *f;
1392         char            buf[256];
1393         char            *cp;
1394         int             exit_status = 0;
1395         int             retval;
1396
1397         if (strcmp(cmd_file, "-") == 0)
1398                 f = stdin;
1399         else {
1400                 f = fopen(cmd_file, "r");
1401                 if (!f) {
1402                         perror(cmd_file);
1403                         exit(1);
1404                 }
1405         }
1406         setbuf(stdout, NULL);
1407         setbuf(stderr, NULL);
1408         while (!feof(f)) {
1409                 if (fgets(buf, sizeof(buf), f) == NULL)
1410                         break;
1411                 cp = strchr(buf, '\n');
1412                 if (cp)
1413                         *cp = 0;
1414                 cp = strchr(buf, '\r');
1415                 if (cp)
1416                         *cp = 0;
1417                 printf("debugfs: %s\n", buf);
1418                 retval = ss_execute_line(sci_idx, buf);
1419                 if (retval) {
1420                         ss_perror(sci_idx, retval, buf);
1421                         exit_status++;
1422                 }
1423         }
1424         return exit_status;
1425 }
1426
1427 int main(int argc, char **argv)
1428 {
1429         int             retval;
1430         int             sci_idx;
1431         const char      *usage = "Usage: debugfs [-b blocksize] [-s superblock] [-f cmd_file] [-R request] [-V] [[-w] [-c] device]";
1432         int             c;
1433         int             open_flags = 0;
1434         char            *request = 0;
1435         int             exit_status = 0;
1436         char            *cmd_file = 0;
1437         blk_t           superblock = 0;
1438         blk_t           blocksize = 0;
1439         int             catastrophic = 0;
1440         
1441         initialize_ext2_error_table();
1442         fprintf (stderr, "debugfs %s (%s)\n", E2FSPROGS_VERSION,
1443                  E2FSPROGS_DATE);
1444
1445         while ((c = getopt (argc, argv, "iwcR:f:b:s:V")) != EOF) {
1446                 switch (c) {
1447                 case 'R':
1448                         request = optarg;
1449                         break;
1450                 case 'f':
1451                         cmd_file = optarg;
1452                         break;
1453                 case 'i':
1454                         open_flags |= EXT2_FLAG_IMAGE_FILE;
1455                         break;
1456                 case 'w':
1457                         open_flags |= EXT2_FLAG_RW;
1458                         break;
1459                 case 'b':
1460                         blocksize = parse_ulong(optarg, argv[0], 
1461                                                 "block size", 0);
1462                         break;
1463                 case 's':
1464                         superblock = parse_ulong(optarg, argv[0], 
1465                                                  "superblock number", 0);
1466                         break;
1467                 case 'c':
1468                         catastrophic = 1;
1469                         break;
1470                 case 'V':
1471                         /* Print version number and exit */
1472                         fprintf(stderr, "\tUsing %s\n",
1473                                 error_message(EXT2_ET_BASE));
1474                         exit(0);
1475                 default:
1476                         com_err(argv[0], 0, usage);
1477                         return 1;
1478                 }
1479         }
1480         if (optind < argc)
1481                 open_filesystem(argv[optind], open_flags,
1482                                 superblock, blocksize, catastrophic);
1483         
1484         sci_idx = ss_create_invocation("debugfs", "0.0", (char *) NULL,
1485                                        &debug_cmds, &retval);
1486         if (retval) {
1487                 ss_perror(sci_idx, retval, "creating invocation");
1488                 exit(1);
1489         }
1490
1491         (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &retval);
1492         if (retval) {
1493                 ss_perror(sci_idx, retval, "adding standard requests");
1494                 exit (1);
1495         }
1496         if (request) {
1497                 retval = 0;
1498                 retval = ss_execute_line(sci_idx, request);
1499                 if (retval) {
1500                         ss_perror(sci_idx, retval, request);
1501                         exit_status++;
1502                 }
1503         } else if (cmd_file) {
1504                 exit_status = source_file(cmd_file, sci_idx);
1505         } else {
1506                 ss_listen(sci_idx);
1507         }
1508
1509         if (current_fs)
1510                 close_filesystem();
1511         
1512         return exit_status;
1513 }