Whamcloud - gitweb
Add to the ss (subsystem) library the ability to dynamically link
[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 = 1;
105 #ifdef HAVE_OPTRESET
106         optreset = 1;           /* Makes BSD getopt happy */
107 #endif
108         while ((c = getopt (argc, argv, "iwfcb:s:")) != EOF) {
109                 switch (c) {
110                 case 'i':
111                         open_flags |= EXT2_FLAG_IMAGE_FILE;
112                         break;
113                 case 'w':
114                         open_flags |= EXT2_FLAG_RW;
115                         break;
116                 case 'f':
117                         open_flags |= EXT2_FLAG_FORCE;
118                         break;
119                 case 'c':
120                         catastrophic = 1;
121                         break;
122                 case 'b':
123                         blocksize = parse_ulong(optarg, argv[0],
124                                                 "block size", &err);
125                         if (err)
126                                 return;
127                         break;
128                 case 's':
129                         superblock = parse_ulong(optarg, argv[0],
130                                                  "superblock number", &err);
131                         if (err)
132                                 return;
133                         break;
134                 default:
135                         com_err(argv[0], 0, usage);
136                         return;
137                 }
138         }
139         if (optind != argc-1) {
140                 com_err(argv[0], 0, usage);
141                 return;
142         }
143         if (check_fs_not_open(argv[0]))
144                 return;
145         open_filesystem(argv[optind], open_flags,
146                         superblock, blocksize, catastrophic);
147 }
148
149 void do_lcd(int argc, char **argv)
150 {
151         if (common_args_process(argc, argv, 2, 2, "lcd",
152                                 "<native dir>", 0))
153                 return;
154
155         if (chdir(argv[1]) == -1) {
156                 com_err(argv[0], errno,
157                         "while trying to change native directory to %s",
158                         argv[1]);
159                 return;
160         }
161 }
162
163 static void close_filesystem(NOARGS)
164 {
165         int     retval;
166         
167         if (current_fs->flags & EXT2_FLAG_IB_DIRTY) {
168                 retval = ext2fs_write_inode_bitmap(current_fs);
169                 if (retval)
170                         com_err("ext2fs_write_inode_bitmap", retval, "");
171         }
172         if (current_fs->flags & EXT2_FLAG_BB_DIRTY) {
173                 retval = ext2fs_write_block_bitmap(current_fs);
174                 if (retval)
175                         com_err("ext2fs_write_block_bitmap", retval, "");
176         }
177         retval = ext2fs_close(current_fs);
178         if (retval)
179                 com_err("ext2fs_close", retval, "");
180         current_fs = NULL;
181         return;
182 }
183
184 void do_close_filesys(int argc, char **argv)
185 {
186         if (common_args_process(argc, argv, 1, 1, "close_filesys", "", 0))
187                 return;
188         close_filesystem();
189 }
190
191 void do_init_filesys(int argc, char **argv)
192 {
193         struct ext2_super_block param;
194         errcode_t       retval;
195         int             err;
196         
197         if (common_args_process(argc, argv, 3, 3, "initialize",
198                                 "<device> <blocksize>", CHECK_FS_NOTOPEN))
199                 return;
200
201         memset(&param, 0, sizeof(struct ext2_super_block));
202         param.s_blocks_count = parse_ulong(argv[2], argv[0],
203                                            "blocks count", &err);
204         if (err)
205                 return;
206         retval = ext2fs_initialize(argv[1], 0, &param,
207                                    unix_io_manager, &current_fs);
208         if (retval) {
209                 com_err(argv[1], retval, "while initializing filesystem");
210                 current_fs = NULL;
211                 return;
212         }
213         root = cwd = EXT2_ROOT_INO;
214         return;
215 }
216
217 static void print_features(struct ext2_super_block * s, FILE *f)
218 {
219         int     i, j, printed=0;
220         __u32   *mask = &s->s_feature_compat, m;
221
222         fputs("Filesystem features:", f);
223         for (i=0; i <3; i++,mask++) {
224                 for (j=0,m=1; j < 32; j++, m<<=1) {
225                         if (*mask & m) {
226                                 fprintf(f, " %s", e2p_feature2string(i, m));
227                                 printed++;
228                         }
229                 }
230         }
231         if (printed == 0)
232                 fputs("(none)", f);
233         fputs("\n", f);
234 }
235
236 void do_show_super_stats(int argc, char *argv[])
237 {
238         int     i;
239         FILE    *out;
240         struct ext2_group_desc *gdp;
241         int     c, header_only = 0;
242         int     numdirs = 0;
243         const char *usage = "Usage: show_super [-h]";
244
245         optind = 1;
246 #ifdef HAVE_OPTRESET
247         optreset = 1;           /* Makes BSD getopt happy */
248 #endif
249         while ((c = getopt (argc, argv, "h")) != EOF) {
250                 switch (c) {
251                 case 'h':
252                         header_only++;
253                         break;
254                 default:
255                         com_err(argv[0], 0, usage);
256                         return;
257                 }
258         }
259         if (optind != argc) {
260                 com_err(argv[0], 0, usage);
261                 return;
262         }
263         if (check_fs_open(argv[0]))
264                 return;
265         out = open_pager();
266
267         list_super2(current_fs->super, out);
268         for (i=0; i < current_fs->group_desc_count; i++)
269                 numdirs += current_fs->group_desc[i].bg_used_dirs_count;
270         fprintf(out, "Directories:              %d\n", numdirs);
271         
272         if (header_only) {
273                 close_pager(out);
274                 return;
275         }
276         
277         gdp = &current_fs->group_desc[0];
278         for (i = 0; i < current_fs->group_desc_count; i++, gdp++)
279                 fprintf(out, " Group %2d: block bitmap at %d, "
280                         "inode bitmap at %d, "
281                         "inode table at %d\n"
282                         "           %d free %s, "
283                         "%d free %s, "
284                         "%d used %s\n",
285                         i, gdp->bg_block_bitmap,
286                         gdp->bg_inode_bitmap, gdp->bg_inode_table,
287                         gdp->bg_free_blocks_count,
288                         gdp->bg_free_blocks_count != 1 ? "blocks" : "block",
289                         gdp->bg_free_inodes_count,
290                         gdp->bg_free_inodes_count != 1 ? "inodes" : "inode",
291                         gdp->bg_used_dirs_count,
292                         gdp->bg_used_dirs_count != 1 ? "directories"
293                                 : "directory");
294         close_pager(out);
295 }
296
297 void do_dirty_filesys(int argc, char **argv)
298 {
299         if (check_fs_open(argv[0]))
300                 return;
301         if (check_fs_read_write(argv[0]))
302                 return;
303
304         if (argv[1] && !strcmp(argv[1], "-clean"))
305                 current_fs->super->s_state |= EXT2_VALID_FS;
306         else
307                 current_fs->super->s_state &= ~EXT2_VALID_FS;
308         ext2fs_mark_super_dirty(current_fs);
309 }
310
311 struct list_blocks_struct {
312         FILE            *f;
313         e2_blkcnt_t     total;
314         blk_t           first_block, last_block;
315         e2_blkcnt_t     first_bcnt, last_bcnt;
316         e2_blkcnt_t     first;
317 };
318
319 static void finish_range(struct list_blocks_struct *lb)
320 {
321         if (lb->first_block == 0)
322                 return;
323         if (lb->first)
324                 lb->first = 0;
325         else
326                 fprintf(lb->f, ", ");
327         if (lb->first_block == lb->last_block)
328                 fprintf(lb->f, "(%lld):%d", lb->first_bcnt, lb->first_block);
329         else
330                 fprintf(lb->f, "(%lld-%lld):%d-%d", lb->first_bcnt,
331                         lb->last_bcnt, lb->first_block, lb->last_block);
332         lb->first_block = 0;
333 }
334
335 static int list_blocks_proc(ext2_filsys fs, blk_t *blocknr,
336                             e2_blkcnt_t blockcnt, blk_t ref_block,
337                             int ref_offset, void *private)
338 {
339         struct list_blocks_struct *lb = (struct list_blocks_struct *) private;
340
341         lb->total++;
342         if (blockcnt >= 0) {
343                 /*
344                  * See if we can add on to the existing range (if it exists)
345                  */
346                 if (lb->first_block &&
347                     (lb->last_block+1 == *blocknr) &&
348                     (lb->last_bcnt+1 == blockcnt)) {
349                         lb->last_block = *blocknr;
350                         lb->last_bcnt = blockcnt;
351                         return 0;
352                 }
353                 /*
354                  * Start a new range.
355                  */
356                 finish_range(lb);
357                 lb->first_block = lb->last_block = *blocknr;
358                 lb->first_bcnt = lb->last_bcnt = blockcnt;
359                 return 0;
360         }
361         /*
362          * Not a normal block.  Always force a new range.
363          */
364         finish_range(lb);
365         if (lb->first)
366                 lb->first = 0;
367         else
368                 fprintf(lb->f, ", ");
369         if (blockcnt == -1)
370                 fprintf(lb->f, "(IND):%d", *blocknr);
371         else if (blockcnt == -2)
372                 fprintf(lb->f, "(DIND):%d", *blocknr);
373         else if (blockcnt == -3)
374                 fprintf(lb->f, "(TIND):%d", *blocknr);
375         return 0;
376 }
377
378
379 static void dump_blocks(FILE *f, const char *prefix, ext2_ino_t inode)
380 {
381         struct list_blocks_struct lb;
382
383         fprintf(f, "%sBLOCKS:\n%s", prefix, prefix);
384         lb.total = 0;
385         lb.first_block = 0;
386         lb.f = f;
387         lb.first = 1;
388         ext2fs_block_iterate2(current_fs, inode, 0, NULL,
389                              list_blocks_proc, (void *)&lb);
390         finish_range(&lb);
391         if (lb.total)
392                 fprintf(f, "\n%sTOTAL: %lld\n", prefix, lb.total);
393         fprintf(f,"\n");
394 }
395
396
397 void internal_dump_inode(FILE *out, const char *prefix,
398                          ext2_ino_t inode_num, struct ext2_inode *inode,
399                          int do_dump_blocks)
400 {
401         const char *i_type;
402         char frag, fsize;
403         int os = current_fs->super->s_creator_os;
404         
405         if (LINUX_S_ISDIR(inode->i_mode)) i_type = "directory";
406         else if (LINUX_S_ISREG(inode->i_mode)) i_type = "regular";
407         else if (LINUX_S_ISLNK(inode->i_mode)) i_type = "symlink";
408         else if (LINUX_S_ISBLK(inode->i_mode)) i_type = "block special";
409         else if (LINUX_S_ISCHR(inode->i_mode)) i_type = "character special";
410         else if (LINUX_S_ISFIFO(inode->i_mode)) i_type = "FIFO";
411         else if (LINUX_S_ISSOCK(inode->i_mode)) i_type = "socket";
412         else i_type = "bad type";
413         fprintf(out, "%sInode: %u   Type: %s    ", prefix, inode_num, i_type);
414         fprintf(out, "%sMode:  %04o   Flags: 0x%x   Generation: %u\n",
415                 prefix, 
416                 inode->i_mode & 0777, inode->i_flags, inode->i_generation);
417         fprintf(out, "%sUser: %5d   Group: %5d   Size: ",
418                 prefix, inode->i_uid, inode->i_gid);
419         if (LINUX_S_ISREG(inode->i_mode)) {
420                 __u64 i_size = (inode->i_size |
421                                 ((unsigned long long)inode->i_size_high << 32));
422
423                 fprintf(out, "%lld\n", i_size);
424         } else
425                 fprintf(out, "%d\n", inode->i_size);
426         if (current_fs->super->s_creator_os == EXT2_OS_HURD)
427                 fprintf(out,
428                         "%sFile ACL: %d    Directory ACL: %d Translator: %d\n",
429                         prefix,
430                         inode->i_file_acl, LINUX_S_ISDIR(inode->i_mode) ? inode->i_dir_acl : 0,
431                         inode->osd1.hurd1.h_i_translator);
432         else
433                 fprintf(out, "%sFile ACL: %d    Directory ACL: %d\n",
434                         prefix,
435                         inode->i_file_acl, LINUX_S_ISDIR(inode->i_mode) ? inode->i_dir_acl : 0);
436         fprintf(out, "%sLinks: %d   Blockcount: %d\n", 
437                 prefix, inode->i_links_count, inode->i_blocks);
438         switch (os) {
439             case EXT2_OS_LINUX:
440                 frag = inode->osd2.linux2.l_i_frag;
441                 fsize = inode->osd2.linux2.l_i_fsize;
442                 break;
443             case EXT2_OS_HURD:
444                 frag = inode->osd2.hurd2.h_i_frag;
445                 fsize = inode->osd2.hurd2.h_i_fsize;
446                 break;
447             case EXT2_OS_MASIX:
448                 frag = inode->osd2.masix2.m_i_frag;
449                 fsize = inode->osd2.masix2.m_i_fsize;
450                 break;
451             default:
452                 frag = fsize = 0;
453         }
454         fprintf(out, "%sFragment:  Address: %d    Number: %d    Size: %d\n",
455                 prefix, inode->i_faddr, frag, fsize);
456         fprintf(out, "%sctime: 0x%08x -- %s", prefix, inode->i_ctime,
457                 time_to_string(inode->i_ctime));
458         fprintf(out, "%satime: 0x%08x -- %s", prefix, inode->i_atime,
459                 time_to_string(inode->i_atime));
460         fprintf(out, "%smtime: 0x%08x -- %s", prefix, inode->i_mtime,
461                 time_to_string(inode->i_mtime));
462         if (inode->i_dtime) 
463           fprintf(out, "%sdtime: 0x%08x -- %s", prefix, inode->i_dtime,
464                   time_to_string(inode->i_dtime));
465         if (LINUX_S_ISLNK(inode->i_mode) && inode->i_blocks == 0)
466                 fprintf(out, "%sFast_link_dest: %.*s\n", prefix,
467                         (int) inode->i_size, (char *)inode->i_block);
468         else if (do_dump_blocks)
469                 dump_blocks(out, prefix, inode_num);
470 }
471
472 static void dump_inode(ext2_ino_t inode_num, struct ext2_inode inode)
473 {
474         FILE    *out;
475         
476         out = open_pager();
477         internal_dump_inode(out, "", inode_num, &inode, 1);
478         close_pager(out);
479 }
480
481 void do_stat(int argc, char *argv[])
482 {
483         ext2_ino_t      inode;
484         struct ext2_inode inode_buf;
485
486         if (common_inode_args_process(argc, argv, &inode, 0))
487                 return;
488
489         if (debugfs_read_inode(inode, &inode_buf, argv[0]))
490                 return;
491
492         dump_inode(inode,inode_buf);
493         return;
494 }
495
496 void do_chroot(int argc, char *argv[])
497 {
498         ext2_ino_t inode;
499         int retval;
500
501         if (common_inode_args_process(argc, argv, &inode, 0))
502                 return;
503
504         retval = ext2fs_check_directory(current_fs, inode);
505         if (retval)  {
506                 com_err(argv[1], retval, "");
507                 return;
508         }
509         root = inode;
510 }
511
512 void do_clri(int argc, char *argv[])
513 {
514         ext2_ino_t inode;
515         struct ext2_inode inode_buf;
516
517         if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
518                 return;
519
520         if (debugfs_read_inode(inode, &inode_buf, argv[0]))
521                 return;
522         memset(&inode_buf, 0, sizeof(inode_buf));
523         if (debugfs_write_inode(inode, &inode_buf, argv[0]))
524                 return;
525 }
526
527 void do_freei(int argc, char *argv[])
528 {
529         ext2_ino_t inode;
530
531         if (common_inode_args_process(argc, argv, &inode,
532                                       CHECK_FS_RW | CHECK_FS_BITMAPS))
533                 return;
534
535         if (!ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
536                 com_err(argv[0], 0, "Warning: inode already clear");
537         ext2fs_unmark_inode_bitmap(current_fs->inode_map,inode);
538         ext2fs_mark_ib_dirty(current_fs);
539 }
540
541 void do_seti(int argc, char *argv[])
542 {
543         ext2_ino_t inode;
544
545         if (common_inode_args_process(argc, argv, &inode,
546                                       CHECK_FS_RW | CHECK_FS_BITMAPS))
547                 return;
548
549         if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
550                 com_err(argv[0], 0, "Warning: inode already set");
551         ext2fs_mark_inode_bitmap(current_fs->inode_map,inode);
552         ext2fs_mark_ib_dirty(current_fs);
553 }
554
555 void do_testi(int argc, char *argv[])
556 {
557         ext2_ino_t inode;
558
559         if (common_inode_args_process(argc, argv, &inode, CHECK_FS_BITMAPS))
560                 return;
561
562         if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
563                 printf("Inode %u is marked in use\n", inode);
564         else
565                 printf("Inode %u is not in use\n", inode);
566 }
567
568 void do_freeb(int argc, char *argv[])
569 {
570         blk_t block;
571         int count = 1;
572
573         if (common_block_args_process(argc, argv, &block, &count))
574                 return;
575         if (check_fs_read_write(argv[0]))
576                 return;
577         while (count-- > 0) {
578                 if (!ext2fs_test_block_bitmap(current_fs->block_map,block))
579                         com_err(argv[0], 0, "Warning: block %d already clear",
580                                 block);
581                 ext2fs_unmark_block_bitmap(current_fs->block_map,block);
582                 block++;
583         }
584         ext2fs_mark_bb_dirty(current_fs);
585 }
586
587 void do_setb(int argc, char *argv[])
588 {
589         blk_t block;
590         int count = 1;
591
592         if (common_block_args_process(argc, argv, &block, &count))
593                 return;
594         if (check_fs_read_write(argv[0]))
595                 return;
596         while (count-- > 0) {
597                 if (ext2fs_test_block_bitmap(current_fs->block_map,block))
598                         com_err(argv[0], 0, "Warning: block %d already set",
599                                 block);
600                 ext2fs_mark_block_bitmap(current_fs->block_map,block);
601                 block++;
602         }
603         ext2fs_mark_bb_dirty(current_fs);
604 }
605
606 void do_testb(int argc, char *argv[])
607 {
608         blk_t block;
609         int count = 1;
610
611         if (common_block_args_process(argc, argv, &block, &count))
612                 return;
613         while (count-- > 0) {
614                 if (ext2fs_test_block_bitmap(current_fs->block_map,block))
615                         printf("Block %d marked in use\n", block);
616                 else
617                         printf("Block %d not in use\n", block);
618                 block++;
619         }
620 }
621
622 static void modify_u8(char *com, const char *prompt,
623                       const char *format, __u8 *val)
624 {
625         char buf[200];
626         unsigned long v;
627         char *tmp;
628
629         sprintf(buf, format, *val);
630         printf("%30s    [%s] ", prompt, buf);
631         fgets(buf, sizeof(buf), stdin);
632         if (buf[strlen (buf) - 1] == '\n')
633                 buf[strlen (buf) - 1] = '\0';
634         if (!buf[0])
635                 return;
636         v = strtoul(buf, &tmp, 0);
637         if (*tmp)
638                 com_err(com, 0, "Bad value - %s", buf);
639         else
640                 *val = v;
641 }
642
643 static void modify_u16(char *com, const char *prompt,
644                        const char *format, __u16 *val)
645 {
646         char buf[200];
647         unsigned long v;
648         char *tmp;
649
650         sprintf(buf, format, *val);
651         printf("%30s    [%s] ", prompt, buf);
652         fgets(buf, sizeof(buf), stdin);
653         if (buf[strlen (buf) - 1] == '\n')
654                 buf[strlen (buf) - 1] = '\0';
655         if (!buf[0])
656                 return;
657         v = strtoul(buf, &tmp, 0);
658         if (*tmp)
659                 com_err(com, 0, "Bad value - %s", buf);
660         else
661                 *val = v;
662 }
663
664 static void modify_u32(char *com, const char *prompt,
665                        const char *format, __u32 *val)
666 {
667         char buf[200];
668         unsigned long v;
669         char *tmp;
670
671         sprintf(buf, format, *val);
672         printf("%30s    [%s] ", prompt, buf);
673         fgets(buf, sizeof(buf), stdin);
674         if (buf[strlen (buf) - 1] == '\n')
675                 buf[strlen (buf) - 1] = '\0';
676         if (!buf[0])
677                 return;
678         v = strtoul(buf, &tmp, 0);
679         if (*tmp)
680                 com_err(com, 0, "Bad value - %s", buf);
681         else
682                 *val = v;
683 }
684
685
686 void do_modify_inode(int argc, char *argv[])
687 {
688         struct ext2_inode inode;
689         ext2_ino_t      inode_num;
690         int             i;
691         unsigned char   *frag, *fsize;
692         char            buf[80];
693         int             os;
694         const char      *hex_format = "0x%x";
695         const char      *octal_format = "0%o";
696         const char      *decimal_format = "%d";
697         
698         if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
699                 return;
700
701         os = current_fs->super->s_creator_os;
702
703         if (debugfs_read_inode(inode_num, &inode, argv[1]))
704                 return;
705         
706         modify_u16(argv[0], "Mode", octal_format, &inode.i_mode);
707         modify_u16(argv[0], "User ID", decimal_format, &inode.i_uid);
708         modify_u16(argv[0], "Group ID", decimal_format, &inode.i_gid);
709         modify_u32(argv[0], "Size", decimal_format, &inode.i_size);
710         modify_u32(argv[0], "Creation time", decimal_format, &inode.i_ctime);
711         modify_u32(argv[0], "Modification time", decimal_format, &inode.i_mtime);
712         modify_u32(argv[0], "Access time", decimal_format, &inode.i_atime);
713         modify_u32(argv[0], "Deletion time", decimal_format, &inode.i_dtime);
714         modify_u16(argv[0], "Link count", decimal_format, &inode.i_links_count);
715         modify_u32(argv[0], "Block count", decimal_format, &inode.i_blocks);
716         modify_u32(argv[0], "File flags", hex_format, &inode.i_flags);
717         modify_u32(argv[0], "Generation", hex_format, &inode.i_generation);
718 #if 0
719         modify_u32(argv[0], "Reserved1", decimal_format, &inode.i_reserved1);
720 #endif
721         modify_u32(argv[0], "File acl", decimal_format, &inode.i_file_acl);
722         if (LINUX_S_ISDIR(inode.i_mode))
723                 modify_u32(argv[0], "Directory acl", decimal_format, &inode.i_dir_acl);
724         else
725                 modify_u32(argv[0], "High 32bits of size", decimal_format, &inode.i_size_high);
726
727         if (current_fs->super->s_creator_os == EXT2_OS_HURD)
728                 modify_u32(argv[0], "Translator Block",
729                             decimal_format, &inode.osd1.hurd1.h_i_translator);
730         
731         modify_u32(argv[0], "Fragment address", decimal_format, &inode.i_faddr);
732         switch (os) {
733             case EXT2_OS_LINUX:
734                 frag = &inode.osd2.linux2.l_i_frag;
735                 fsize = &inode.osd2.linux2.l_i_fsize;
736                 break;
737             case EXT2_OS_HURD:
738                 frag = &inode.osd2.hurd2.h_i_frag;
739                 fsize = &inode.osd2.hurd2.h_i_fsize;
740                 break;
741             case EXT2_OS_MASIX:
742                 frag = &inode.osd2.masix2.m_i_frag;
743                 fsize = &inode.osd2.masix2.m_i_fsize;
744                 break;
745             default:
746                 frag = fsize = 0;
747         }
748         if (frag)
749                 modify_u8(argv[0], "Fragment number", decimal_format, frag);
750         if (fsize)
751                 modify_u8(argv[0], "Fragment size", decimal_format, fsize);
752
753         for (i=0;  i < EXT2_NDIR_BLOCKS; i++) {
754                 sprintf(buf, "Direct Block #%d", i);
755                 modify_u32(argv[0], buf, decimal_format, &inode.i_block[i]);
756         }
757         modify_u32(argv[0], "Indirect Block", decimal_format,
758                     &inode.i_block[EXT2_IND_BLOCK]);    
759         modify_u32(argv[0], "Double Indirect Block", decimal_format,
760                     &inode.i_block[EXT2_DIND_BLOCK]);
761         modify_u32(argv[0], "Triple Indirect Block", decimal_format,
762                     &inode.i_block[EXT2_TIND_BLOCK]);
763         if (debugfs_write_inode(inode_num, &inode, argv[1]))
764                 return;
765 }
766
767 void do_change_working_dir(int argc, char *argv[])
768 {
769         ext2_ino_t      inode;
770         int             retval;
771         
772         if (common_inode_args_process(argc, argv, &inode, 0))
773                 return;
774
775         retval = ext2fs_check_directory(current_fs, inode);
776         if (retval) {
777                 com_err(argv[1], retval, "");
778                 return;
779         }
780         cwd = inode;
781         return;
782 }
783
784 void do_print_working_directory(int argc, char *argv[])
785 {
786         int     retval;
787         char    *pathname = NULL;
788         
789         if (common_args_process(argc, argv, 1, 1,
790                                 "print_working_directory", "", 0))
791                 return;
792
793         retval = ext2fs_get_pathname(current_fs, cwd, 0, &pathname);
794         if (retval) {
795                 com_err(argv[0], retval,
796                         "while trying to get pathname of cwd");
797         }
798         printf("[pwd]   INODE: %6u  PATH: %s\n", cwd, pathname);
799         free(pathname);
800         retval = ext2fs_get_pathname(current_fs, root, 0, &pathname);
801         if (retval) {
802                 com_err(argv[0], retval,
803                         "while trying to get pathname of root");
804         }
805         printf("[root]  INODE: %6u  PATH: %s\n", root, pathname);
806         free(pathname);
807         return;
808 }
809
810 static void make_link(char *sourcename, char *destname)
811 {
812         ext2_ino_t      inode;
813         int             retval;
814         ext2_ino_t      dir;
815         char            *dest, *cp, *basename;
816
817         /*
818          * Get the source inode
819          */
820         inode = string_to_inode(sourcename);
821         if (!inode)
822                 return;
823         basename = strrchr(sourcename, '/');
824         if (basename)
825                 basename++;
826         else
827                 basename = sourcename;
828         /*
829          * Figure out the destination.  First see if it exists and is
830          * a directory.  
831          */
832         if (! (retval=ext2fs_namei(current_fs, root, cwd, destname, &dir)))
833                 dest = basename;
834         else {
835                 /*
836                  * OK, it doesn't exist.  See if it is
837                  * '<dir>/basename' or 'basename'
838                  */
839                 cp = strrchr(destname, '/');
840                 if (cp) {
841                         *cp = 0;
842                         dir = string_to_inode(destname);
843                         if (!dir)
844                                 return;
845                         dest = cp+1;
846                 } else {
847                         dir = cwd;
848                         dest = destname;
849                 }
850         }
851         
852         retval = ext2fs_link(current_fs, dir, dest, inode, 0);
853         if (retval)
854                 com_err("make_link", retval, "");
855         return;
856 }
857
858
859 void do_link(int argc, char *argv[])
860 {
861         if (common_args_process(argc, argv, 3, 3, "link",
862                                 "<source file> <dest_name>", CHECK_FS_RW))
863                 return;
864
865         make_link(argv[1], argv[2]);
866 }
867
868 static int mark_blocks_proc(ext2_filsys fs, blk_t *blocknr,
869                             int blockcnt, void *private)
870 {
871         blk_t   block;
872
873         block = *blocknr;
874         ext2fs_block_alloc_stats(fs, block, +1);
875         return 0;
876 }
877
878 void do_undel(int argc, char *argv[])
879 {
880         ext2_ino_t      ino;
881         struct ext2_inode inode;
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_stats2(current_fs, ino, +1, 0);
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
1085         if (common_args_process(argc, argv, 3, 3, "write",
1086                                 "<native file> <new file>", CHECK_FS_RW))
1087                 return;
1088
1089         fd = open(argv[1], O_RDONLY);
1090         if (fd < 0) {
1091                 com_err(argv[1], errno, "");
1092                 return;
1093         }
1094         if (fstat(fd, &statbuf) < 0) {
1095                 com_err(argv[1], errno, "");
1096                 close(fd);
1097                 return;
1098         }
1099
1100         retval = ext2fs_namei(current_fs, root, cwd, argv[2], &newfile);
1101         if (retval == 0) {
1102                 com_err(argv[0], 0, "The file '%s' already exists\n", argv[2]);
1103                 close(fd);
1104                 return;
1105         }
1106
1107         retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
1108         if (retval) {
1109                 com_err(argv[0], retval, "");
1110                 close(fd);
1111                 return;
1112         }
1113         printf("Allocated inode: %u\n", newfile);
1114         retval = ext2fs_link(current_fs, cwd, argv[2], newfile,
1115                              EXT2_FT_REG_FILE);
1116         if (retval) {
1117                 com_err(argv[2], retval, "");
1118                 close(fd);
1119                 return;
1120         }
1121         if (ext2fs_test_inode_bitmap(current_fs->inode_map,newfile))
1122                 com_err(argv[0], 0, "Warning: inode already set");
1123         ext2fs_inode_alloc_stats2(current_fs, newfile, +1, 0);
1124         memset(&inode, 0, sizeof(inode));
1125         inode.i_mode = statbuf.st_mode;
1126         inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
1127         inode.i_links_count = 1;
1128         inode.i_size = statbuf.st_size;
1129         if (debugfs_write_inode(newfile, &inode, argv[0])) {
1130                 close(fd);
1131                 return;
1132         }
1133         if (LINUX_S_ISREG(inode.i_mode)) {
1134                 retval = copy_file(fd, newfile);
1135                 if (retval)
1136                         com_err("copy_file", retval, "");
1137         }
1138         close(fd);
1139 }
1140
1141 void do_mknod(int argc, char *argv[])
1142 {
1143         unsigned long   mode, major, minor, nr;
1144         ext2_ino_t      newfile;
1145         errcode_t       retval;
1146         struct ext2_inode inode;
1147         int             filetype;
1148
1149         if (check_fs_open(argv[0]))
1150                 return;
1151         if (argc < 3 || argv[2][1]) {
1152         usage:
1153                 com_err(argv[0], 0, "Usage: mknod <name> [p| [c|b] <major> <minor>]");
1154                 return;
1155         }
1156         mode = minor = major = 0;
1157         switch (argv[2][0]) {
1158                 case 'p':
1159                         mode = LINUX_S_IFIFO;
1160                         filetype = EXT2_FT_FIFO;
1161                         nr = 3;
1162                         break;
1163                 case 'c':
1164                         mode = LINUX_S_IFCHR;
1165                         filetype = EXT2_FT_CHRDEV;
1166                         nr = 5;
1167                         break;
1168                 case 'b':
1169                         mode = LINUX_S_IFBLK;
1170                         filetype = EXT2_FT_BLKDEV;
1171                         nr = 5;
1172                         break;
1173                 default:
1174                         filetype = 0;
1175                         nr = 0;
1176         }
1177         if (nr == 5) {
1178                 major = strtoul(argv[3], argv+3, 0);
1179                 minor = strtoul(argv[4], argv+4, 0);
1180                 if (major > 255 || minor > 255 || argv[3][0] || argv[4][0])
1181                         nr = 0;
1182         }
1183         if (argc != nr)
1184                 goto usage;
1185         if (check_fs_read_write(argv[0]))
1186                 return;
1187         retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
1188         if (retval) {
1189                 com_err(argv[0], retval, "");
1190                 return;
1191         }
1192         printf("Allocated inode: %u\n", newfile);
1193         retval = ext2fs_link(current_fs, cwd, argv[1], newfile, filetype);
1194         if (retval) {
1195                 if (retval == EXT2_ET_DIR_NO_SPACE) {
1196                         retval = ext2fs_expand_dir(current_fs, cwd);
1197                         if (!retval)
1198                                 retval = ext2fs_link(current_fs, cwd,
1199                                                      argv[1], newfile,
1200                                                      filetype);
1201                 }
1202                 if (retval) {
1203                         com_err(argv[1], retval, "");
1204                         return;
1205                 }
1206         }
1207         if (ext2fs_test_inode_bitmap(current_fs->inode_map,newfile))
1208                 com_err(argv[0], 0, "Warning: inode already set");
1209         ext2fs_mark_inode_bitmap(current_fs->inode_map, newfile);
1210         ext2fs_mark_ib_dirty(current_fs);
1211         memset(&inode, 0, sizeof(inode));
1212         inode.i_mode = mode;
1213         inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
1214         inode.i_block[0] = major*256+minor;
1215         inode.i_links_count = 1;
1216         if (debugfs_write_inode(newfile, &inode, argv[0]))
1217                 return;
1218 }
1219
1220 void do_mkdir(int argc, char *argv[])
1221 {
1222         char    *cp;
1223         ext2_ino_t      parent;
1224         char    *name;
1225         errcode_t retval;
1226
1227         if (common_args_process(argc, argv, 2, 2, "mkdir",
1228                                 "<filename>", CHECK_FS_RW))
1229                 return;
1230
1231         cp = strrchr(argv[1], '/');
1232         if (cp) {
1233                 *cp = 0;
1234                 parent = string_to_inode(argv[1]);
1235                 if (!parent) {
1236                         com_err(argv[1], ENOENT, "");
1237                         return;
1238                 }
1239                 name = cp+1;
1240         } else {
1241                 parent = cwd;
1242                 name = argv[1];
1243         }
1244
1245
1246         retval = ext2fs_mkdir(current_fs, parent, 0, name);
1247         if (retval) {
1248                 com_err("ext2fs_mkdir", retval, "");
1249                 return;
1250         }
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
1259         block = *blocknr;
1260         ext2fs_block_alloc_stats(fs, block, -1);
1261         return 0;
1262 }
1263
1264 static void kill_file_by_inode(ext2_ino_t inode)
1265 {
1266         struct ext2_inode inode_buf;
1267
1268         if (debugfs_read_inode(inode, &inode_buf, 0))
1269                 return;
1270         inode_buf.i_dtime = time(NULL);
1271         if (debugfs_write_inode(inode, &inode_buf, 0))
1272                 return;
1273
1274         ext2fs_block_iterate(current_fs, inode, 0, NULL,
1275                              release_blocks_proc, NULL);
1276         printf("\n");
1277         ext2fs_inode_alloc_stats2(current_fs, inode, -1,
1278                                   LINUX_S_ISDIR(inode_buf.i_mode));
1279 }
1280
1281
1282 void do_kill_file(int argc, char *argv[])
1283 {
1284         ext2_ino_t inode_num;
1285
1286         if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
1287                 return;
1288
1289         kill_file_by_inode(inode_num);
1290 }
1291
1292 void do_rm(int argc, char *argv[])
1293 {
1294         int retval;
1295         ext2_ino_t inode_num;
1296         struct ext2_inode inode;
1297
1298         if (common_args_process(argc, argv, 2, 2, "rm",
1299                                 "<filename>", CHECK_FS_RW))
1300                 return;
1301
1302         retval = ext2fs_namei(current_fs, root, cwd, argv[1], &inode_num);
1303         if (retval) {
1304                 com_err(argv[0], retval, "while trying to resolve filename");
1305                 return;
1306         }
1307
1308         if (debugfs_read_inode(inode_num, &inode, argv[0]))
1309                 return;
1310
1311         if (LINUX_S_ISDIR(inode.i_mode)) {
1312                 com_err(argv[0], 0, "file is a directory");
1313                 return;
1314         }
1315
1316         --inode.i_links_count;
1317         if (debugfs_write_inode(inode_num, &inode, argv[0]))
1318                 return;
1319
1320         unlink_file_by_name(argv[1]);
1321         if (inode.i_links_count == 0)
1322                 kill_file_by_inode(inode_num);
1323 }
1324
1325 struct rd_struct {
1326         ext2_ino_t      parent;
1327         int             empty;
1328 };
1329
1330 static int rmdir_proc(ext2_ino_t dir,
1331                       int       entry,
1332                       struct ext2_dir_entry *dirent,
1333                       int       offset,
1334                       int       blocksize,
1335                       char      *buf,
1336                       void      *private)
1337 {
1338         struct rd_struct *rds = (struct rd_struct *) private;
1339
1340         if (dirent->inode == 0)
1341                 return 0;
1342         if (((dirent->name_len&0xFF) == 1) && (dirent->name[0] == '.'))
1343                 return 0;
1344         if (((dirent->name_len&0xFF) == 2) && (dirent->name[0] == '.') &&
1345             (dirent->name[1] == '.')) {
1346                 rds->parent = dirent->inode;
1347                 return 0;
1348         }
1349         rds->empty = 0;
1350         return 0;
1351 }
1352         
1353 void do_rmdir(int argc, char *argv[])
1354 {
1355         int retval;
1356         ext2_ino_t inode_num;
1357         struct ext2_inode inode;
1358         struct rd_struct rds;
1359
1360         if (common_args_process(argc, argv, 2, 2, "rmdir",
1361                                 "<filename>", CHECK_FS_RW))
1362                 return;
1363
1364         retval = ext2fs_namei(current_fs, root, cwd, argv[1], &inode_num);
1365         if (retval) {
1366                 com_err(argv[0], retval, "while trying to resolve filename");
1367                 return;
1368         }
1369
1370         if (debugfs_read_inode(inode_num, &inode, argv[0]))
1371                 return;
1372
1373         if (!LINUX_S_ISDIR(inode.i_mode)) {
1374                 com_err(argv[0], 0, "file is not a directory");
1375                 return;
1376         }
1377
1378         rds.parent = 0;
1379         rds.empty = 1;
1380
1381         retval = ext2fs_dir_iterate2(current_fs, inode_num, 0,
1382                                     0, rmdir_proc, &rds);
1383         if (retval) {
1384                 com_err(argv[0], retval, "while iterating over directory");
1385                 return;
1386         }
1387         if (rds.empty == 0) {
1388                 com_err(argv[0], 0, "directory not empty");
1389                 return;
1390         }
1391
1392         inode.i_links_count = 0;
1393         if (debugfs_write_inode(inode_num, &inode, argv[0]))
1394                 return;
1395
1396         unlink_file_by_name(argv[1]);
1397         kill_file_by_inode(inode_num);
1398
1399         if (rds.parent) {
1400                 if (debugfs_read_inode(rds.parent, &inode, argv[0]))
1401                         return;
1402                 if (inode.i_links_count > 1)
1403                         inode.i_links_count--;
1404                 if (debugfs_write_inode(rds.parent, &inode, argv[0]))
1405                         return;
1406         }
1407 }
1408
1409 void do_show_debugfs_params(int argc, char *argv[])
1410 {
1411         FILE *out = stdout;
1412
1413         if (current_fs)
1414                 fprintf(out, "Open mode: read-%s\n",
1415                         current_fs->flags & EXT2_FLAG_RW ? "write" : "only");
1416         fprintf(out, "Filesystem in use: %s\n",
1417                 current_fs ? current_fs->device_name : "--none--");
1418 }
1419
1420 void do_expand_dir(int argc, char *argv[])
1421 {
1422         ext2_ino_t inode;
1423         int retval;
1424
1425         if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
1426                 return;
1427
1428         retval = ext2fs_expand_dir(current_fs, inode);
1429         if (retval)
1430                 com_err("ext2fs_expand_dir", retval, "");
1431         return;
1432 }
1433
1434 void do_features(int argc, char *argv[])
1435 {
1436         int     i;
1437         
1438         if (check_fs_open(argv[0]))
1439                 return;
1440
1441         if ((argc != 1) && check_fs_read_write(argv[0]))
1442                 return;
1443         for (i=1; i < argc; i++) {
1444                 if (e2p_edit_feature(argv[i],
1445                                      &current_fs->super->s_feature_compat, 0))
1446                         com_err(argv[0], 0, "Unknown feature: %s\n",
1447                                 argv[i]);
1448                 else
1449                         ext2fs_mark_super_dirty(current_fs);
1450         }
1451         print_features(current_fs->super, stdout);
1452 }
1453
1454 void do_bmap(int argc, char *argv[])
1455 {
1456         ext2_ino_t      ino;
1457         blk_t           blk, pblk;
1458         int             err;
1459         errcode_t       errcode;
1460         
1461         if (common_args_process(argc, argv, 3, 3, argv[0],
1462                                 "<file> logical_blk", 0))
1463                 return;
1464
1465         ino = string_to_inode(argv[1]);
1466         blk = parse_ulong(argv[2], argv[0], "logical_block", &err);
1467
1468         errcode = ext2fs_bmap(current_fs, ino, 0, 0, 0, blk, &pblk);
1469         if (errcode) {
1470                 com_err("argv[0]", errcode,
1471                         "while mapping logical block %d\n", blk);
1472                 return;
1473         }
1474         printf("%d\n", pblk);
1475 }
1476
1477
1478 static int source_file(const char *cmd_file, int sci_idx)
1479 {
1480         FILE            *f;
1481         char            buf[256];
1482         char            *cp;
1483         int             exit_status = 0;
1484         int             retval;
1485
1486         if (strcmp(cmd_file, "-") == 0)
1487                 f = stdin;
1488         else {
1489                 f = fopen(cmd_file, "r");
1490                 if (!f) {
1491                         perror(cmd_file);
1492                         exit(1);
1493                 }
1494         }
1495         setbuf(stdout, NULL);
1496         setbuf(stderr, NULL);
1497         while (!feof(f)) {
1498                 if (fgets(buf, sizeof(buf), f) == NULL)
1499                         break;
1500                 cp = strchr(buf, '\n');
1501                 if (cp)
1502                         *cp = 0;
1503                 cp = strchr(buf, '\r');
1504                 if (cp)
1505                         *cp = 0;
1506                 printf("debugfs: %s\n", buf);
1507                 retval = ss_execute_line(sci_idx, buf);
1508                 if (retval) {
1509                         ss_perror(sci_idx, retval, buf);
1510                         exit_status++;
1511                 }
1512         }
1513         return exit_status;
1514 }
1515
1516 int main(int argc, char **argv)
1517 {
1518         int             retval;
1519         int             sci_idx;
1520         const char      *usage = "Usage: debugfs [-b blocksize] [-s superblock] [-f cmd_file] [-R request] [-V] [[-w] [-c] device]";
1521         int             c;
1522         int             open_flags = 0;
1523         char            *request = 0;
1524         int             exit_status = 0;
1525         char            *cmd_file = 0;
1526         blk_t           superblock = 0;
1527         blk_t           blocksize = 0;
1528         int             catastrophic = 0;
1529         
1530         initialize_ext2_error_table();
1531         fprintf (stderr, "debugfs %s (%s)\n", E2FSPROGS_VERSION,
1532                  E2FSPROGS_DATE);
1533
1534         while ((c = getopt (argc, argv, "iwcR:f:b:s:V")) != EOF) {
1535                 switch (c) {
1536                 case 'R':
1537                         request = optarg;
1538                         break;
1539                 case 'f':
1540                         cmd_file = optarg;
1541                         break;
1542                 case 'i':
1543                         open_flags |= EXT2_FLAG_IMAGE_FILE;
1544                         break;
1545                 case 'w':
1546                         open_flags |= EXT2_FLAG_RW;
1547                         break;
1548                 case 'b':
1549                         blocksize = parse_ulong(optarg, argv[0], 
1550                                                 "block size", 0);
1551                         break;
1552                 case 's':
1553                         superblock = parse_ulong(optarg, argv[0], 
1554                                                  "superblock number", 0);
1555                         break;
1556                 case 'c':
1557                         catastrophic = 1;
1558                         break;
1559                 case 'V':
1560                         /* Print version number and exit */
1561                         fprintf(stderr, "\tUsing %s\n",
1562                                 error_message(EXT2_ET_BASE));
1563                         exit(0);
1564                 default:
1565                         com_err(argv[0], 0, usage);
1566                         return 1;
1567                 }
1568         }
1569         if (optind < argc)
1570                 open_filesystem(argv[optind], open_flags,
1571                                 superblock, blocksize, catastrophic);
1572         
1573         sci_idx = ss_create_invocation("debugfs", "0.0", (char *) NULL,
1574                                        &debug_cmds, &retval);
1575         if (retval) {
1576                 ss_perror(sci_idx, retval, "creating invocation");
1577                 exit(1);
1578         }
1579         ss_get_readline(sci_idx);
1580
1581         (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &retval);
1582         if (retval) {
1583                 ss_perror(sci_idx, retval, "adding standard requests");
1584                 exit (1);
1585         }
1586         if (request) {
1587                 retval = 0;
1588                 retval = ss_execute_line(sci_idx, request);
1589                 if (retval) {
1590                         ss_perror(sci_idx, retval, request);
1591                         exit_status++;
1592                 }
1593         } else if (cmd_file) {
1594                 exit_status = source_file(cmd_file, sci_idx);
1595         } else {
1596                 ss_listen(sci_idx);
1597         }
1598
1599         if (current_fs)
1600                 close_filesystem();
1601         
1602         return exit_status;
1603 }