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