Whamcloud - gitweb
df08610260aa71f2257188585cc5825ed9728bc8
[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[0], argv[2],
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         int             retval;
482
483         if (common_inode_args_process(argc, argv, &inode, 0))
484                 return;
485
486         if (debugfs_read_inode(inode, &inode_buf, argv[0]))
487                 return;
488
489         dump_inode(inode,inode_buf);
490         return;
491 }
492
493 void do_chroot(int argc, char *argv[])
494 {
495         ext2_ino_t inode;
496         int retval;
497
498         if (common_inode_args_process(argc, argv, &inode, 0))
499                 return;
500
501         retval = ext2fs_check_directory(current_fs, inode);
502         if (retval)  {
503                 com_err(argv[1], retval, "");
504                 return;
505         }
506         root = inode;
507 }
508
509 void do_clri(int argc, char *argv[])
510 {
511         ext2_ino_t inode;
512         int retval;
513         struct ext2_inode inode_buf;
514
515         if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
516                 return;
517
518         if (debugfs_read_inode(inode, &inode_buf, argv[0]))
519                 return;
520         memset(&inode_buf, 0, sizeof(inode_buf));
521         if (debugfs_write_inode(inode, &inode_buf, argv[0]))
522                 return;
523 }
524
525 void do_freei(int argc, char *argv[])
526 {
527         ext2_ino_t inode;
528
529         if (common_inode_args_process(argc, argv, &inode,
530                                       CHECK_FS_RW | CHECK_FS_BITMAPS))
531                 return;
532
533         if (!ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
534                 com_err(argv[0], 0, "Warning: inode already clear");
535         ext2fs_unmark_inode_bitmap(current_fs->inode_map,inode);
536         ext2fs_mark_ib_dirty(current_fs);
537 }
538
539 void do_seti(int argc, char *argv[])
540 {
541         ext2_ino_t inode;
542
543         if (common_inode_args_process(argc, argv, &inode,
544                                       CHECK_FS_RW | CHECK_FS_BITMAPS))
545                 return;
546
547         if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
548                 com_err(argv[0], 0, "Warning: inode already set");
549         ext2fs_mark_inode_bitmap(current_fs->inode_map,inode);
550         ext2fs_mark_ib_dirty(current_fs);
551 }
552
553 void do_testi(int argc, char *argv[])
554 {
555         ext2_ino_t inode;
556
557         if (common_inode_args_process(argc, argv, &inode, CHECK_FS_BITMAPS))
558                 return;
559
560         if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
561                 printf("Inode %u is marked in use\n", inode);
562         else
563                 printf("Inode %u is not in use\n", inode);
564 }
565
566 void do_freeb(int argc, char *argv[])
567 {
568         blk_t block;
569         int count = 1;
570
571         if (common_block_args_process(argc, argv, &block, &count))
572                 return;
573         if (check_fs_read_write(argv[0]))
574                 return;
575         while (count-- > 0) {
576                 if (!ext2fs_test_block_bitmap(current_fs->block_map,block))
577                         com_err(argv[0], 0, "Warning: block %d already clear",
578                                 block);
579                 ext2fs_unmark_block_bitmap(current_fs->block_map,block);
580                 block++;
581         }
582         ext2fs_mark_bb_dirty(current_fs);
583 }
584
585 void do_setb(int argc, char *argv[])
586 {
587         blk_t block;
588         int count = 1;
589
590         if (common_block_args_process(argc, argv, &block, &count))
591                 return;
592         if (check_fs_read_write(argv[0]))
593                 return;
594         while (count-- > 0) {
595                 if (ext2fs_test_block_bitmap(current_fs->block_map,block))
596                         com_err(argv[0], 0, "Warning: block %d already set",
597                                 block);
598                 ext2fs_mark_block_bitmap(current_fs->block_map,block);
599                 block++;
600         }
601         ext2fs_mark_bb_dirty(current_fs);
602 }
603
604 void do_testb(int argc, char *argv[])
605 {
606         blk_t block;
607         int count = 1;
608
609         if (common_block_args_process(argc, argv, &block, &count))
610                 return;
611         while (count-- > 0) {
612                 if (ext2fs_test_block_bitmap(current_fs->block_map,block))
613                         printf("Block %d marked in use\n", block);
614                 else
615                         printf("Block %d not in use\n", block);
616                 block++;
617         }
618 }
619
620 static void modify_u8(char *com, const char *prompt,
621                       const char *format, __u8 *val)
622 {
623         char buf[200];
624         unsigned long v;
625         char *tmp;
626
627         sprintf(buf, format, *val);
628         printf("%30s    [%s] ", prompt, buf);
629         fgets(buf, sizeof(buf), stdin);
630         if (buf[strlen (buf) - 1] == '\n')
631                 buf[strlen (buf) - 1] = '\0';
632         if (!buf[0])
633                 return;
634         v = strtoul(buf, &tmp, 0);
635         if (*tmp)
636                 com_err(com, 0, "Bad value - %s", buf);
637         else
638                 *val = v;
639 }
640
641 static void modify_u16(char *com, const char *prompt,
642                        const char *format, __u16 *val)
643 {
644         char buf[200];
645         unsigned long v;
646         char *tmp;
647
648         sprintf(buf, format, *val);
649         printf("%30s    [%s] ", prompt, buf);
650         fgets(buf, sizeof(buf), stdin);
651         if (buf[strlen (buf) - 1] == '\n')
652                 buf[strlen (buf) - 1] = '\0';
653         if (!buf[0])
654                 return;
655         v = strtoul(buf, &tmp, 0);
656         if (*tmp)
657                 com_err(com, 0, "Bad value - %s", buf);
658         else
659                 *val = v;
660 }
661
662 static void modify_u32(char *com, const char *prompt,
663                        const char *format, __u32 *val)
664 {
665         char buf[200];
666         unsigned long v;
667         char *tmp;
668
669         sprintf(buf, format, *val);
670         printf("%30s    [%s] ", prompt, buf);
671         fgets(buf, sizeof(buf), stdin);
672         if (buf[strlen (buf) - 1] == '\n')
673                 buf[strlen (buf) - 1] = '\0';
674         if (!buf[0])
675                 return;
676         v = strtoul(buf, &tmp, 0);
677         if (*tmp)
678                 com_err(com, 0, "Bad value - %s", buf);
679         else
680                 *val = v;
681 }
682
683
684 void do_modify_inode(int argc, char *argv[])
685 {
686         struct ext2_inode inode;
687         ext2_ino_t      inode_num;
688         int             i;
689         errcode_t       retval;
690         unsigned char   *frag, *fsize;
691         char            buf[80];
692         int             os = current_fs->super->s_creator_os;
693         const char      *hex_format = "0x%x";
694         const char      *octal_format = "0%o";
695         const char      *decimal_format = "%d";
696         
697         if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
698                 return;
699
700         if (debugfs_read_inode(inode_num, &inode, argv[1]))
701                 return;
702         
703         modify_u16(argv[0], "Mode", octal_format, &inode.i_mode);
704         modify_u16(argv[0], "User ID", decimal_format, &inode.i_uid);
705         modify_u16(argv[0], "Group ID", decimal_format, &inode.i_gid);
706         modify_u32(argv[0], "Size", decimal_format, &inode.i_size);
707         modify_u32(argv[0], "Creation time", decimal_format, &inode.i_ctime);
708         modify_u32(argv[0], "Modification time", decimal_format, &inode.i_mtime);
709         modify_u32(argv[0], "Access time", decimal_format, &inode.i_atime);
710         modify_u32(argv[0], "Deletion time", decimal_format, &inode.i_dtime);
711         modify_u16(argv[0], "Link count", decimal_format, &inode.i_links_count);
712         modify_u32(argv[0], "Block count", decimal_format, &inode.i_blocks);
713         modify_u32(argv[0], "File flags", hex_format, &inode.i_flags);
714         modify_u32(argv[0], "Generation", hex_format, &inode.i_generation);
715 #if 0
716         modify_u32(argv[0], "Reserved1", decimal_format, &inode.i_reserved1);
717 #endif
718         modify_u32(argv[0], "File acl", decimal_format, &inode.i_file_acl);
719         if (LINUX_S_ISDIR(inode.i_mode))
720                 modify_u32(argv[0], "Directory acl", decimal_format, &inode.i_dir_acl);
721         else
722                 modify_u32(argv[0], "High 32bits of size", decimal_format, &inode.i_size_high);
723
724         if (current_fs->super->s_creator_os == EXT2_OS_HURD)
725                 modify_u32(argv[0], "Translator Block",
726                             decimal_format, &inode.osd1.hurd1.h_i_translator);
727         
728         modify_u32(argv[0], "Fragment address", decimal_format, &inode.i_faddr);
729         switch (os) {
730             case EXT2_OS_LINUX:
731                 frag = &inode.osd2.linux2.l_i_frag;
732                 fsize = &inode.osd2.linux2.l_i_fsize;
733                 break;
734             case EXT2_OS_HURD:
735                 frag = &inode.osd2.hurd2.h_i_frag;
736                 fsize = &inode.osd2.hurd2.h_i_fsize;
737                 break;
738             case EXT2_OS_MASIX:
739                 frag = &inode.osd2.masix2.m_i_frag;
740                 fsize = &inode.osd2.masix2.m_i_fsize;
741                 break;
742             default:
743                 frag = fsize = 0;
744         }
745         if (frag)
746                 modify_u8(argv[0], "Fragment number", decimal_format, frag);
747         if (fsize)
748                 modify_u8(argv[0], "Fragment size", decimal_format, fsize);
749
750         for (i=0;  i < EXT2_NDIR_BLOCKS; i++) {
751                 sprintf(buf, "Direct Block #%d", i);
752                 modify_u32(argv[0], buf, decimal_format, &inode.i_block[i]);
753         }
754         modify_u32(argv[0], "Indirect Block", decimal_format,
755                     &inode.i_block[EXT2_IND_BLOCK]);    
756         modify_u32(argv[0], "Double Indirect Block", decimal_format,
757                     &inode.i_block[EXT2_DIND_BLOCK]);
758         modify_u32(argv[0], "Triple Indirect Block", decimal_format,
759                     &inode.i_block[EXT2_TIND_BLOCK]);
760         if (debugfs_write_inode(inode_num, &inode, argv[1]))
761                 return;
762 }
763
764 void do_change_working_dir(int argc, char *argv[])
765 {
766         ext2_ino_t      inode;
767         int             retval;
768         
769         if (common_inode_args_process(argc, argv, &inode, 0))
770                 return;
771
772         retval = ext2fs_check_directory(current_fs, inode);
773         if (retval) {
774                 com_err(argv[1], retval, "");
775                 return;
776         }
777         cwd = inode;
778         return;
779 }
780
781 void do_print_working_directory(int argc, char *argv[])
782 {
783         int     retval;
784         char    *pathname = NULL;
785         
786         if (common_args_process(argc, argv, 1, 1,
787                                 "print_working_directory", "", 0))
788                 return;
789
790         retval = ext2fs_get_pathname(current_fs, cwd, 0, &pathname);
791         if (retval) {
792                 com_err(argv[0], retval,
793                         "while trying to get pathname of cwd");
794         }
795         printf("[pwd]   INODE: %6u  PATH: %s\n", cwd, pathname);
796         free(pathname);
797         retval = ext2fs_get_pathname(current_fs, root, 0, &pathname);
798         if (retval) {
799                 com_err(argv[0], retval,
800                         "while trying to get pathname of root");
801         }
802         printf("[root]  INODE: %6u  PATH: %s\n", root, pathname);
803         free(pathname);
804         return;
805 }
806
807 static void make_link(char *sourcename, char *destname)
808 {
809         ext2_ino_t      inode;
810         int             retval;
811         ext2_ino_t      dir;
812         char            *dest, *cp, *basename;
813
814         /*
815          * Get the source inode
816          */
817         inode = string_to_inode(sourcename);
818         if (!inode)
819                 return;
820         basename = strrchr(sourcename, '/');
821         if (basename)
822                 basename++;
823         else
824                 basename = sourcename;
825         /*
826          * Figure out the destination.  First see if it exists and is
827          * a directory.  
828          */
829         if (! (retval=ext2fs_namei(current_fs, root, cwd, destname, &dir)))
830                 dest = basename;
831         else {
832                 /*
833                  * OK, it doesn't exist.  See if it is
834                  * '<dir>/basename' or 'basename'
835                  */
836                 cp = strrchr(destname, '/');
837                 if (cp) {
838                         *cp = 0;
839                         dir = string_to_inode(destname);
840                         if (!dir)
841                                 return;
842                         dest = cp+1;
843                 } else {
844                         dir = cwd;
845                         dest = destname;
846                 }
847         }
848         
849         retval = ext2fs_link(current_fs, dir, dest, inode, 0);
850         if (retval)
851                 com_err("make_link", retval, "");
852         return;
853 }
854
855
856 void do_link(int argc, char *argv[])
857 {
858         if (common_args_process(argc, argv, 3, 3, "link",
859                                 "<source file> <dest_name>", CHECK_FS_RW))
860                 return;
861
862         make_link(argv[1], argv[2]);
863 }
864
865 static int mark_blocks_proc(ext2_filsys fs, blk_t *blocknr,
866                             int blockcnt, void *private)
867 {
868         blk_t   block;
869         dgrp_t  group;
870
871         block = *blocknr;
872         ext2fs_block_alloc_stats(fs, block, +1);
873         return 0;
874 }
875
876 void do_undel(int argc, char *argv[])
877 {
878         ext2_ino_t      ino;
879         struct ext2_inode inode;
880         int             retval;
881         dgrp_t group;
882
883         if (common_args_process(argc, argv, 3, 3, "undelete",
884                                 "<inode_num> <dest_name>",
885                                 CHECK_FS_RW | CHECK_FS_BITMAPS))
886                 return;
887
888         ino = string_to_inode(argv[1]);
889         if (!ino)
890                 return;
891
892         if (debugfs_read_inode(ino, &inode, argv[1]))
893                 return;
894
895         if (ext2fs_test_inode_bitmap(current_fs->inode_map, ino)) {
896                 com_err(argv[1], 0, "Inode is not marked as deleted");
897                 return;
898         }
899
900         /*
901          * XXX this function doesn't handle changing the links count on the
902          * parent directory when undeleting a directory.  
903          */
904         inode.i_links_count = LINUX_S_ISDIR(inode.i_mode) ? 2 : 1;
905         inode.i_dtime = 0;
906
907         if (debugfs_write_inode(ino, &inode, argv[0]))
908                 return;
909
910         ext2fs_block_iterate(current_fs, ino, 0, NULL,
911                              mark_blocks_proc, NULL);
912
913         ext2fs_inode_alloc_stats(current_fs, ino, +1);
914
915         make_link(argv[1], argv[2]);
916 }
917
918 static void unlink_file_by_name(char *filename)
919 {
920         int             retval;
921         ext2_ino_t      dir;
922         char            *basename;
923         
924         basename = strrchr(filename, '/');
925         if (basename) {
926                 *basename++ = '\0';
927                 dir = string_to_inode(filename);
928                 if (!dir)
929                         return;
930         } else {
931                 dir = cwd;
932                 basename = filename;
933         }
934         retval = ext2fs_unlink(current_fs, dir, basename, 0, 0);
935         if (retval)
936                 com_err("unlink_file_by_name", retval, "");
937         return;
938 }
939
940 void do_unlink(int argc, char *argv[])
941 {
942         if (common_args_process(argc, argv, 2, 2, "link",
943                                 "<pathname>", CHECK_FS_RW))
944                 return;
945
946         unlink_file_by_name(argv[1]);
947 }
948
949 void do_find_free_block(int argc, char *argv[])
950 {
951         blk_t   free_blk, goal;
952         int             count;
953         errcode_t       retval;
954         char            *tmp;
955         
956         if ((argc > 3) || (argc==2 && *argv[1] == '?')) {
957                 com_err(argv[0], 0, "Usage: find_free_block [count [goal]]");
958                 return;
959         }
960         if (check_fs_open(argv[0]))
961                 return;
962
963         if (argc > 1) {
964                 count = strtol(argv[1],&tmp,0);
965                 if (*tmp) {
966                         com_err(argv[0], 0, "Bad count - %s", argv[1]);
967                         return;
968                 }
969         } else
970                 count = 1;
971
972         if (argc > 2) {
973                 goal = strtol(argv[2], &tmp, 0);
974                 if (*tmp) {
975                         com_err(argv[0], 0, "Bad goal - %s", argv[1]);
976                         return;
977                 }
978         }
979         else
980                 goal = current_fs->super->s_first_data_block;
981
982         printf("Free blocks found: ");
983         free_blk = goal - 1;    
984         while (count-- > 0) {
985                 retval = ext2fs_new_block(current_fs, free_blk + 1, 0,
986                                           &free_blk);
987                 if (retval) {
988                         com_err("ext2fs_new_block", retval, "");
989                         return;
990                 } else
991                         printf("%d ", free_blk);
992         }
993         printf("\n");
994 }
995
996 void do_find_free_inode(int argc, char *argv[])
997 {
998         ext2_ino_t      free_inode, dir;
999         int             mode;
1000         int             retval;
1001         char            *tmp;
1002         
1003         if (argc > 3 || (argc>1 && *argv[1] == '?')) {
1004                 com_err(argv[0], 0, "Usage: find_free_inode [dir] [mode]");
1005                 return;
1006         }
1007         if (check_fs_open(argv[0]))
1008                 return;
1009
1010         if (argc > 1) {
1011                 dir = strtol(argv[1], &tmp, 0);
1012                 if (*tmp) {
1013                         com_err(argv[0], 0, "Bad dir - %s", argv[1]);
1014                         return;
1015                 }
1016         }
1017         else
1018                 dir = root;
1019         if (argc > 2) {
1020                 mode = strtol(argv[2], &tmp, 0);
1021                 if (*tmp) {
1022                         com_err(argv[0], 0, "Bad mode - %s", argv[2]);
1023                         return;
1024                 }
1025         } else
1026                 mode = 010755;
1027
1028         retval = ext2fs_new_inode(current_fs, dir, mode, 0, &free_inode);
1029         if (retval)
1030                 com_err("ext2fs_new_inode", retval, "");
1031         else
1032                 printf("Free inode found: %u\n", free_inode);
1033 }
1034
1035 static errcode_t copy_file(int fd, ext2_ino_t newfile)
1036 {
1037         ext2_file_t     e2_file;
1038         errcode_t       retval;
1039         int             got;
1040         unsigned int    written;
1041         char            buf[8192];
1042         char            *ptr;
1043
1044         retval = ext2fs_file_open(current_fs, newfile,
1045                                   EXT2_FILE_WRITE, &e2_file);
1046         if (retval)
1047                 return retval;
1048
1049         while (1) {
1050                 got = read(fd, buf, sizeof(buf));
1051                 if (got == 0)
1052                         break;
1053                 if (got < 0) {
1054                         retval = errno;
1055                         goto fail;
1056                 }
1057                 ptr = buf;
1058                 while (got > 0) {
1059                         retval = ext2fs_file_write(e2_file, ptr,
1060                                                    got, &written);
1061                         if (retval)
1062                                 goto fail;
1063
1064                         got -= written;
1065                         ptr += written;
1066                 }
1067         }
1068         retval = ext2fs_file_close(e2_file);
1069         return retval;
1070
1071 fail:
1072         (void) ext2fs_file_close(e2_file);
1073         return retval;
1074 }
1075
1076
1077 void do_write(int argc, char *argv[])
1078 {
1079         int             fd;
1080         struct stat     statbuf;
1081         ext2_ino_t      newfile;
1082         errcode_t       retval;
1083         struct ext2_inode inode;
1084         dgrp_t          group;
1085
1086         if (common_args_process(argc, argv, 3, 3, "write",
1087                                 "<native file> <new file>", CHECK_FS_RW))
1088                 return;
1089
1090         fd = open(argv[1], O_RDONLY);
1091         if (fd < 0) {
1092                 com_err(argv[1], errno, "");
1093                 return;
1094         }
1095         if (fstat(fd, &statbuf) < 0) {
1096                 com_err(argv[1], errno, "");
1097                 close(fd);
1098                 return;
1099         }
1100
1101         retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
1102         if (retval) {
1103                 com_err(argv[0], retval, "");
1104                 close(fd);
1105                 return;
1106         }
1107         printf("Allocated inode: %u\n", newfile);
1108         retval = ext2fs_link(current_fs, cwd, argv[2], newfile,
1109                              EXT2_FT_REG_FILE);
1110         if (retval) {
1111                 com_err(argv[2], retval, "");
1112                 close(fd);
1113                 return;
1114         }
1115         if (ext2fs_test_inode_bitmap(current_fs->inode_map,newfile))
1116                 com_err(argv[0], 0, "Warning: inode already set");
1117         ext2fs_inode_alloc_stats(current_fs, newfile, +1);
1118         memset(&inode, 0, sizeof(inode));
1119         inode.i_mode = statbuf.st_mode;
1120         inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
1121         inode.i_links_count = 1;
1122         inode.i_size = statbuf.st_size;
1123         if (debugfs_write_inode(newfile, &inode, argv[0])) {
1124                 close(fd);
1125                 return;
1126         }
1127         if (LINUX_S_ISREG(inode.i_mode)) {
1128                 retval = copy_file(fd, newfile);
1129                 if (retval)
1130                         com_err("copy_file", retval, "");
1131         }
1132         close(fd);
1133 }
1134
1135 void do_mknod(int argc, char *argv[])
1136 {
1137         unsigned long   mode, major, minor, nr;
1138         ext2_ino_t      newfile;
1139         errcode_t       retval;
1140         struct ext2_inode inode;
1141         int             filetype;
1142
1143         if (check_fs_open(argv[0]))
1144                 return;
1145         if (argc < 3 || argv[2][1]) {
1146         usage:
1147                 com_err(argv[0], 0, "Usage: mknod <name> [p| [c|b] <major> <minor>]");
1148                 return;
1149         }
1150         mode = minor = major = 0;
1151         switch (argv[2][0]) {
1152                 case 'p':
1153                         mode = LINUX_S_IFIFO;
1154                         filetype = EXT2_FT_FIFO;
1155                         nr = 3;
1156                         break;
1157                 case 'c':
1158                         mode = LINUX_S_IFCHR;
1159                         filetype = EXT2_FT_CHRDEV;
1160                         nr = 5;
1161                         break;
1162                 case 'b':
1163                         mode = LINUX_S_IFBLK;
1164                         filetype = EXT2_FT_BLKDEV;
1165                         nr = 5;
1166                         break;
1167                 default:
1168                         filetype = 0;
1169                         nr = 0;
1170         }
1171         if (nr == 5) {
1172                 major = strtoul(argv[3], argv+3, 0);
1173                 minor = strtoul(argv[4], argv+4, 0);
1174                 if (major > 255 || minor > 255 || argv[3][0] || argv[4][0])
1175                         nr = 0;
1176         }
1177         if (argc != nr)
1178                 goto usage;
1179         if (check_fs_read_write(argv[0]))
1180                 return;
1181         retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
1182         if (retval) {
1183                 com_err(argv[0], retval, "");
1184                 return;
1185         }
1186         printf("Allocated inode: %u\n", newfile);
1187         retval = ext2fs_link(current_fs, cwd, argv[1], newfile, filetype);
1188         if (retval) {
1189                 if (retval == EXT2_ET_DIR_NO_SPACE) {
1190                         retval = ext2fs_expand_dir(current_fs, cwd);
1191                         if (!retval)
1192                                 retval = ext2fs_link(current_fs, cwd,
1193                                                      argv[1], newfile,
1194                                                      filetype);
1195                 }
1196                 if (retval) {
1197                         com_err(argv[1], retval, "");
1198                         return;
1199                 }
1200         }
1201         if (ext2fs_test_inode_bitmap(current_fs->inode_map,newfile))
1202                 com_err(argv[0], 0, "Warning: inode already set");
1203         ext2fs_mark_inode_bitmap(current_fs->inode_map, newfile);
1204         ext2fs_mark_ib_dirty(current_fs);
1205         memset(&inode, 0, sizeof(inode));
1206         inode.i_mode = mode;
1207         inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
1208         inode.i_block[0] = major*256+minor;
1209         inode.i_links_count = 1;
1210         if (debugfs_write_inode(newfile, &inode, argv[0]))
1211                 return;
1212 }
1213
1214 void do_mkdir(int argc, char *argv[])
1215 {
1216         char    *cp;
1217         ext2_ino_t      parent;
1218         char    *name;
1219         errcode_t retval;
1220
1221         if (common_args_process(argc, argv, 2, 2, "mkdir",
1222                                 "<filename>", CHECK_FS_RW))
1223                 return;
1224
1225         cp = strrchr(argv[1], '/');
1226         if (cp) {
1227                 *cp = 0;
1228                 parent = string_to_inode(argv[1]);
1229                 if (!parent) {
1230                         com_err(argv[1], ENOENT, "");
1231                         return;
1232                 }
1233                 name = cp+1;
1234         } else {
1235                 parent = cwd;
1236                 name = argv[1];
1237         }
1238
1239
1240         retval = ext2fs_mkdir(current_fs, parent, 0, name);
1241         if (retval) {
1242                 com_err("ext2fs_mkdir", retval, "");
1243                 return;
1244         }
1245
1246 }
1247
1248 void do_rmdir(int argc, char *argv[])
1249 {
1250         printf("Unimplemented\n");
1251 }
1252
1253
1254 static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
1255                                int blockcnt, void *private)
1256 {
1257         blk_t   block;
1258         dgrp_t  group;
1259
1260         block = *blocknr;
1261         ext2fs_block_alloc_stats(fs, block, -1);
1262         return 0;
1263 }
1264
1265 static void kill_file_by_inode(ext2_ino_t inode)
1266 {
1267         struct ext2_inode inode_buf;
1268         int     group;
1269
1270         if (debugfs_read_inode(inode, &inode_buf, 0))
1271                 return;
1272         inode_buf.i_dtime = time(NULL);
1273         if (debugfs_write_inode(inode, &inode_buf, 0))
1274                 return;
1275
1276         printf("Kill file by inode %u\n", inode);
1277         ext2fs_block_iterate(current_fs, inode, 0, NULL,
1278                              release_blocks_proc, NULL);
1279         printf("\n");
1280         ext2fs_inode_alloc_stats(current_fs, inode, -1);
1281 }
1282
1283
1284 void do_kill_file(int argc, char *argv[])
1285 {
1286         ext2_ino_t inode_num;
1287
1288         if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
1289                 return;
1290
1291         kill_file_by_inode(inode_num);
1292 }
1293
1294 void do_rm(int argc, char *argv[])
1295 {
1296         int retval;
1297         ext2_ino_t inode_num;
1298         struct ext2_inode inode;
1299
1300         if (common_args_process(argc, argv, 2, 2, "rm",
1301                                 "<filename>", CHECK_FS_RW))
1302                 return;
1303
1304         retval = ext2fs_namei(current_fs, root, cwd, argv[1], &inode_num);
1305         if (retval) {
1306                 com_err(argv[0], retval, "while trying to resolve filename");
1307                 return;
1308         }
1309
1310         if (debugfs_read_inode(inode_num, &inode, argv[0]))
1311                 return;
1312
1313         if (LINUX_S_ISDIR(inode.i_mode)) {
1314                 com_err(argv[0], 0, "file is a directory");
1315                 return;
1316         }
1317
1318         --inode.i_links_count;
1319         if (debugfs_write_inode(inode_num, &inode, argv[0]))
1320                 return;
1321
1322         unlink_file_by_name(argv[1]);
1323         if (inode.i_links_count == 0)
1324                 kill_file_by_inode(inode_num);
1325 }
1326
1327 void do_show_debugfs_params(int argc, char *argv[])
1328 {
1329         FILE *out = stdout;
1330
1331         if (current_fs)
1332                 fprintf(out, "Open mode: read-%s\n",
1333                         current_fs->flags & EXT2_FLAG_RW ? "write" : "only");
1334         fprintf(out, "Filesystem in use: %s\n",
1335                 current_fs ? current_fs->device_name : "--none--");
1336 }
1337
1338 void do_expand_dir(int argc, char *argv[])
1339 {
1340         ext2_ino_t inode;
1341         int retval;
1342
1343         if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
1344                 return;
1345
1346         retval = ext2fs_expand_dir(current_fs, inode);
1347         if (retval)
1348                 com_err("ext2fs_expand_dir", retval, "");
1349         return;
1350 }
1351
1352 void do_features(int argc, char *argv[])
1353 {
1354         int     i;
1355         
1356         if (check_fs_open(argv[0]))
1357                 return;
1358
1359         if ((argc != 1) && check_fs_read_write(argv[0]))
1360                 return;
1361         for (i=1; i < argc; i++) {
1362                 if (e2p_edit_feature(argv[i],
1363                                      &current_fs->super->s_feature_compat, 0))
1364                         com_err(argv[0], 0, "Unknown feature: %s\n",
1365                                 argv[i]);
1366                 else
1367                         ext2fs_mark_super_dirty(current_fs);
1368         }
1369         print_features(current_fs->super, stdout);
1370 }
1371
1372 static int source_file(const char *cmd_file, int sci_idx)
1373 {
1374         FILE            *f;
1375         char            buf[256];
1376         char            *cp;
1377         int             exit_status = 0;
1378         int             retval;
1379
1380         if (strcmp(cmd_file, "-") == 0)
1381                 f = stdin;
1382         else {
1383                 f = fopen(cmd_file, "r");
1384                 if (!f) {
1385                         perror(cmd_file);
1386                         exit(1);
1387                 }
1388         }
1389         setbuf(stdout, NULL);
1390         setbuf(stderr, NULL);
1391         while (!feof(f)) {
1392                 if (fgets(buf, sizeof(buf), f) == NULL)
1393                         break;
1394                 cp = strchr(buf, '\n');
1395                 if (cp)
1396                         *cp = 0;
1397                 cp = strchr(buf, '\r');
1398                 if (cp)
1399                         *cp = 0;
1400                 printf("debugfs: %s\n", buf);
1401                 retval = ss_execute_line(sci_idx, buf);
1402                 if (retval) {
1403                         ss_perror(sci_idx, retval, buf);
1404                         exit_status++;
1405                 }
1406         }
1407         return exit_status;
1408 }
1409
1410 int main(int argc, char **argv)
1411 {
1412         int             retval;
1413         int             sci_idx;
1414         const char      *usage = "Usage: debugfs [-b blocksize] [-s superblock] [-f cmd_file] [-R request] [-V] [[-w] [-c] device]";
1415         int             c;
1416         int             open_flags = 0;
1417         char            *request = 0;
1418         int             exit_status = 0;
1419         char            *cmd_file = 0;
1420         blk_t           superblock = 0;
1421         blk_t           blocksize = 0;
1422         int             catastrophic = 0;
1423         
1424         initialize_ext2_error_table();
1425         fprintf (stderr, "debugfs %s (%s)\n", E2FSPROGS_VERSION,
1426                  E2FSPROGS_DATE);
1427
1428         while ((c = getopt (argc, argv, "iwcR:f:b:s:V")) != EOF) {
1429                 switch (c) {
1430                 case 'R':
1431                         request = optarg;
1432                         break;
1433                 case 'f':
1434                         cmd_file = optarg;
1435                         break;
1436                 case 'i':
1437                         open_flags |= EXT2_FLAG_IMAGE_FILE;
1438                         break;
1439                 case 'w':
1440                         open_flags |= EXT2_FLAG_RW;
1441                         break;
1442                 case 'b':
1443                         blocksize = parse_ulong(argv[0], optarg,
1444                                                 "block size", 0);
1445                         break;
1446                 case 's':
1447                         superblock = parse_ulong(argv[0], optarg,
1448                                                  "superblock number", 0);
1449                         break;
1450                 case 'c':
1451                         catastrophic = 1;
1452                         break;
1453                 case 'V':
1454                         /* Print version number and exit */
1455                         fprintf(stderr, "\tUsing %s\n",
1456                                 error_message(EXT2_ET_BASE));
1457                         exit(0);
1458                 default:
1459                         com_err(argv[0], 0, usage);
1460                         return 1;
1461                 }
1462         }
1463         if (optind < argc)
1464                 open_filesystem(argv[optind], open_flags,
1465                                 superblock, blocksize, catastrophic);
1466         
1467         sci_idx = ss_create_invocation("debugfs", "0.0", (char *) NULL,
1468                                        &debug_cmds, &retval);
1469         if (retval) {
1470                 ss_perror(sci_idx, retval, "creating invocation");
1471                 exit(1);
1472         }
1473
1474         (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &retval);
1475         if (retval) {
1476                 ss_perror(sci_idx, retval, "adding standard requests");
1477                 exit (1);
1478         }
1479         if (request) {
1480                 retval = 0;
1481                 retval = ss_execute_line(sci_idx, request);
1482                 if (retval) {
1483                         ss_perror(sci_idx, retval, request);
1484                         exit_status++;
1485                 }
1486         } else if (cmd_file) {
1487                 exit_status = source_file(cmd_file, sci_idx);
1488         } else {
1489                 ss_listen(sci_idx);
1490         }
1491
1492         if (current_fs)
1493                 close_filesystem();
1494         
1495         return exit_status;
1496 }