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