Whamcloud - gitweb
Many files:
[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 #include <getopt.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20
21 #include "et/com_err.h"
22 #include "ss/ss.h"
23 #include "debugfs.h"
24
25 extern ss_request_table debug_cmds;
26
27 ext2_filsys fs = NULL;
28 ino_t   root, cwd;
29
30 void open_filesystem(char *device, int open_flags)
31 {
32         int     retval;
33         
34         retval = ext2fs_open(device, open_flags, 0, 0, unix_io_manager, &fs);
35         if (retval) {
36                 com_err(device, retval, "while opening filesystem");
37                 fs = NULL;
38                 return;
39         }
40         retval = ext2fs_read_inode_bitmap(fs);
41         if (retval) {
42                 com_err(device, retval, "while reading inode bitmap");
43                 goto errout;
44         }
45         retval = ext2fs_read_block_bitmap(fs);
46         if (retval) {
47                 com_err(device, retval, "while reading block bitmap");
48                 goto errout;
49         }
50         root = cwd = EXT2_ROOT_INO;
51         return;
52
53 errout:
54         retval = ext2fs_close(fs);
55         if (retval)
56                 com_err(device, retval, "while trying to close filesystem");
57         fs = NULL;
58 }
59
60 void do_open_filesys(int argc, char **argv)
61 {
62         char    *usage = "Usage: open [-w] <device>";
63         char    c;
64         int open_flags = 0;
65         
66         optind = 0;
67         while ((c = getopt (argc, argv, "w")) != EOF) {
68                 switch (c) {
69                 case 'w':
70                         open_flags = EXT2_FLAG_RW;
71                         break;
72                 default:
73                         com_err(argv[0], 0, usage);
74                         return;
75                 }
76         }
77         if (optind != argc-1) {
78                 com_err(argv[0], 0, usage);
79                 return;
80         }
81         if (check_fs_not_open(argv[0]))
82                 return;
83         open_filesystem(argv[optind], open_flags);
84 }
85
86 void close_filesystem()
87 {
88         int     retval;
89         
90         if (fs->flags & EXT2_FLAG_IB_DIRTY) {
91                 retval = ext2fs_write_inode_bitmap(fs);
92                 if (retval)
93                         com_err("ext2fs_write_inode_bitmap", retval, "");
94         }
95         if (fs->flags & EXT2_FLAG_BB_DIRTY) {
96                 retval = ext2fs_write_block_bitmap(fs);
97                 if (retval)
98                         com_err("ext2fs_write_block_bitmap", retval, "");
99         }
100         retval = ext2fs_close(fs);
101         if (retval)
102                 com_err("ext2fs_close", retval, "");
103         fs = NULL;
104         return;
105 }
106
107 void do_close_filesys(int argc, char **argv)
108 {
109         if (argc > 1) {
110                 com_err(argv[0], 0, "Usage: close_filesys");
111                 return;
112         }
113         if (check_fs_open(argv[0]))
114                 return;
115         close_filesystem();
116 }
117
118 void do_init_filesys(int argc, char **argv)
119 {
120         char    *usage = "Usage: initialize <device> <blocksize>";
121         struct ext2_super_block param;
122         errcode_t       retval;
123         char            *tmp;
124         
125         if (argc != 3) {
126                 com_err(argv[0], 0, usage);
127                 return;
128         }
129         if (check_fs_not_open(argv[0]))
130                 return;
131
132         memset(&param, 0, sizeof(struct ext2_super_block));
133         param.s_blocks_count = strtoul(argv[2], &tmp, 0);
134         if (*tmp) {
135                 com_err(argv[0], 0, "Bad blocks count - %s", argv[2]);
136                 return;
137         }
138         retval = ext2fs_initialize(argv[1], 0, &param, unix_io_manager, &fs);
139         if (retval) {
140                 com_err(argv[1], retval, "while initializing filesystem");
141                 fs = NULL;
142                 return;
143         }
144         root = cwd = EXT2_ROOT_INO;
145         return;
146 }
147
148 void do_show_super_stats(int argc, char *argv[])
149 {
150         int     i;
151         FILE    *out;
152
153         if (argc > 1) {
154                 com_err(argv[0], 0, "Usage: show_super");
155                 return;
156         }
157         if (check_fs_open(argv[0]))
158                 return;
159         out = open_pager();
160         fprintf(out, "Filesystem is read-%s\n", fs->flags & EXT2_FLAG_RW ?
161                 "write" : "only");
162         fprintf(out, "Last mount time = %s", ctime(&fs->super->s_mtime));
163         fprintf(out, "Last write time = %s", ctime(&fs->super->s_wtime));
164         fprintf(out, "Mount counts = %d (maximal = %d)\n",
165                 fs->super->s_mnt_count, fs->super->s_max_mnt_count);
166         fprintf(out, "Superblock size = %d\n", sizeof(struct ext2_super_block));
167         fprintf(out, "Block size = %d, fragment size = %d\n",
168                 EXT2_BLOCK_SIZE(fs->super), EXT2_FRAG_SIZE(fs->super));
169         fprintf(out, "%ld inodes, %ld free\n", fs->super->s_inodes_count,
170                 fs->super->s_free_inodes_count);
171         fprintf(out, "%ld blocks, %ld free, %ld reserved, first block = %ld\n",
172                 fs->super->s_blocks_count, fs->super->s_free_blocks_count,
173                 fs->super->s_r_blocks_count, fs->super->s_first_data_block);
174         fprintf(out, "%ld blocks per group\n", fs->super->s_blocks_per_group);
175         fprintf(out, "%ld fragments per group\n", fs->super->s_frags_per_group);
176         fprintf(out, "%ld inodes per group\n", EXT2_INODES_PER_GROUP(fs->super));
177         fprintf(out, "%d inodes per block\n", EXT2_INODES_PER_BLOCK(fs->super));
178         fprintf(out, "%ld group%s (%ld descriptors block%s)\n",
179                 fs->group_desc_count, (fs->group_desc_count != 1) ? "s" : "",
180                 fs->desc_blocks, (fs->desc_blocks != 1) ? "s" : "");
181         for (i = 0; i < fs->group_desc_count; i++)
182                 fprintf(out, " Group %2d: block bitmap at %ld, "
183                         "inode bitmap at %ld, "
184                         "inode table at %ld\n"
185                         "           %d free block%s, "
186                         "%d free inode%s, "
187                         "%d used director%s\n",
188                         i, fs->group_desc[i].bg_block_bitmap,
189                         fs->group_desc[i].bg_inode_bitmap,
190                         fs->group_desc[i].bg_inode_table,
191                         fs->group_desc[i].bg_free_blocks_count,
192                         fs->group_desc[i].bg_free_blocks_count != 1 ? "s" : "",
193                         fs->group_desc[i].bg_free_inodes_count,
194                         fs->group_desc[i].bg_free_inodes_count != 1 ? "s" : "",
195                         fs->group_desc[i].bg_used_dirs_count,
196                         fs->group_desc[i].bg_used_dirs_count != 1 ? "ies" : "y");
197         close_pager(out);
198 }
199
200 struct list_blocks_struct {
201         FILE    *f;
202         int     total;
203 };
204
205 int list_blocks_proc(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
206 {
207         struct list_blocks_struct *lb = (struct list_blocks_struct *) private;
208
209         fprintf(lb->f, "%ld ", *blocknr);
210         lb->total++;
211         return 0;
212 }
213
214
215 void dump_blocks(FILE *f, ino_t inode)
216 {
217         struct list_blocks_struct lb;
218
219         fprintf(f, "BLOCKS:\n");
220         lb.total = 0;
221         lb.f = f;
222         ext2fs_block_iterate(fs,inode,0,NULL,list_blocks_proc,(void *)&lb);
223         if (lb.total)
224                 fprintf(f, "\nTOTAL: %d\n", lb.total);
225         fprintf(f,"\n");
226 }
227
228
229 void dump_inode(ino_t inode_num, struct ext2_inode inode)
230 {
231         char *i_type;
232         FILE    *out;
233         
234         out = open_pager();
235         if (S_ISDIR(inode.i_mode)) i_type = "directory";
236         else if (S_ISREG(inode.i_mode)) i_type = "regular";
237         else if (S_ISLNK(inode.i_mode)) i_type = "symlink";
238         else if (S_ISBLK(inode.i_mode)) i_type = "block special";
239         else if (S_ISCHR(inode.i_mode)) i_type = "character special";
240         else if (S_ISFIFO(inode.i_mode)) i_type = "FIFO";
241         else if (S_ISSOCK(inode.i_mode)) i_type = "socket";
242         else i_type = "bad type";
243         fprintf(out, "Inode: %ld   Type: %s    ", inode_num, i_type);
244         fprintf(out, "Mode:  %04o   Flags: 0x%lx   Version: %ld\n",
245                 inode.i_mode & 0777, inode.i_flags, inode.i_version);
246         fprintf(out, "User: %5d   Group: %5d   Size: %ld\n",  
247                 inode.i_uid, inode.i_gid, inode.i_size);
248         fprintf(out, "File ACL: %ld    Directory ACL: %ld\n",
249                 inode.i_file_acl, inode.i_dir_acl);
250         fprintf(out, "Links: %d   Blockcount: %ld\n", inode.i_links_count,
251                 inode.i_blocks);
252         fprintf(out, "Fragment:  Address: %ld    Number: %d    Size: %d\n",
253                 inode.i_faddr, inode.i_frag, inode.i_fsize);
254         fprintf(out, "ctime: 0x%08lx -- %s", inode.i_ctime,
255                 ctime(&inode.i_ctime));
256         fprintf(out, "atime: 0x%08lx -- %s", inode.i_atime,
257                 ctime(&inode.i_atime));
258         fprintf(out, "mtime: 0x%08lx -- %s", inode.i_mtime,
259                 ctime(&inode.i_mtime));
260         if (inode.i_dtime) 
261           fprintf(out, "dtime: 0x%08lx -- %s", inode.i_dtime,
262                   ctime(&inode.i_dtime));
263         if (S_ISLNK(inode.i_mode) && inode.i_blocks == 0)
264                 fprintf(out, "Fast_link_dest: %s\n", (char *)inode.i_block);
265         else
266                 dump_blocks(out, inode_num);
267         close_pager(out);
268 }
269
270
271 void do_stat(int argc, char *argv[])
272 {
273         ino_t   inode;
274         struct ext2_inode inode_buf;
275         int retval;
276
277         if (argc != 2) {
278                 com_err(argv[0], 0, "Usage: stat <file>");
279                 return;
280         }
281         if (check_fs_open(argv[0]))
282                 return;
283         inode = string_to_inode(argv[1]);
284         if (!inode) 
285                 return;
286
287         retval = ext2fs_read_inode(fs,inode,&inode_buf);
288         if (retval) 
289           {
290             com_err(argv[0], 0, "Reading inode");
291             return;
292           }
293
294         dump_inode(inode,inode_buf);
295         return;
296 }
297
298 void do_chroot(int argc, char *argv[])
299 {
300         ino_t inode;
301         int retval;
302
303         if (argc != 2) {
304                 com_err(argv[0], 0, "Usage: chroot <file>");
305                 return;
306         }
307         if (check_fs_open(argv[0]))
308                 return;
309         inode = string_to_inode(argv[1]);
310         if (!inode) 
311                 return;
312
313         retval = ext2fs_check_directory(fs, inode);
314         if (retval)  {
315                 com_err(argv[1], retval, "");
316                 return;
317         }
318         root = inode;
319 }
320
321 void do_clri(int argc, char *argv[])
322 {
323         ino_t inode;
324         int retval;
325         struct ext2_inode inode_buf;
326
327         if (argc != 2) {
328                 com_err(argv[0], 0, "Usage: clri <file>");
329                 return;
330         }
331         if (check_fs_open(argv[0]))
332                 return;
333         if (!(fs->flags & EXT2_FLAG_RW)) {
334                 com_err(argv[0], 0, "Filesystem opened read/only");
335                 return;
336         }
337         inode = string_to_inode(argv[1]);
338         if (!inode) 
339                 return;
340
341         retval = ext2fs_read_inode(fs, inode, &inode_buf);
342         if (retval) {
343                 com_err(argv[0], 0, "while trying to read inode %d", inode);
344                 return;
345         }
346         memset(&inode_buf, 0, sizeof(inode_buf));
347         retval = ext2fs_write_inode(fs, inode, &inode_buf);
348         if (retval) {
349                 com_err(argv[0], retval, "while trying to write inode %d",
350                         inode);
351                 return;
352         }
353 }
354
355 void do_freei(int argc, char *argv[])
356 {
357         ino_t inode;
358
359         if (argc != 2) {
360                 com_err(argv[0], 0, "Usage: freei <file>");
361                 return;
362         }
363         if (check_fs_open(argv[0]))
364                 return;
365         if (!(fs->flags & EXT2_FLAG_RW)) {
366                 com_err(argv[0], 0, "Filesystem opened read/only");
367                 return;
368         }
369         inode = string_to_inode(argv[1]);
370         if (!inode) 
371                 return;
372
373         if (!ext2fs_test_inode_bitmap(fs->inode_map,inode))
374                 com_err(argv[0], 0, "Warning: inode already clear");
375         ext2fs_unmark_inode_bitmap(fs->inode_map,inode);
376         ext2fs_mark_ib_dirty(fs);
377 }
378
379 void do_seti(int argc, char *argv[])
380 {
381         ino_t inode;
382
383         if (argc != 2) {
384                 com_err(argv[0], 0, "Usage: seti <file>");
385                 return;
386         }
387         if (check_fs_open(argv[0]))
388                 return;
389         if (!(fs->flags & EXT2_FLAG_RW)) {
390                 com_err(argv[0], 0, "Filesystem opened read/only");
391                 return;
392         }
393         inode = string_to_inode(argv[1]);
394         if (!inode) 
395                 return;
396
397         if (ext2fs_test_inode_bitmap(fs->inode_map,inode))
398                 com_err(argv[0], 0, "Warning: inode already set");
399         ext2fs_mark_inode_bitmap(fs->inode_map,inode);
400         ext2fs_mark_ib_dirty(fs);
401 }
402
403 void do_testi(int argc, char *argv[])
404 {
405         ino_t inode;
406
407         if (argc != 2) {
408                 com_err(argv[0], 0, "Usage: testi <file>");
409                 return;
410         }
411         if (check_fs_open(argv[0]))
412                 return;
413         inode = string_to_inode(argv[1]);
414         if (!inode) 
415                 return;
416
417         if (ext2fs_test_inode_bitmap(fs->inode_map,inode))
418                 printf("Inode %ld is marked in use\n", inode);
419         else
420                 printf("Inode %ld is not in use\n", inode);
421 }
422
423
424 void do_freeb(int argc, char *argv[])
425 {
426         blk_t block;
427         char *tmp;
428
429         if (argc != 2) {
430                 com_err(argv[0], 0, "Usage: freeb <block>");
431                 return;
432         }
433         if (check_fs_open(argv[0]))
434                 return;
435         if (!(fs->flags & EXT2_FLAG_RW)) {
436                 com_err(argv[0], 0, "Filesystem opened read/only");
437                 return;
438         }
439         block = strtoul(argv[1], &tmp, 0);
440         if (!block || *tmp) {
441                 com_err(argv[0], 0, "No block 0");
442                 return;
443         } 
444         if (!ext2fs_test_block_bitmap(fs->block_map,block))
445                 com_err(argv[0], 0, "Warning: block already clear");
446         ext2fs_unmark_block_bitmap(fs->block_map,block);
447         ext2fs_mark_bb_dirty(fs);
448 }
449
450 void do_setb(int argc, char *argv[])
451 {
452         blk_t block;
453         char *tmp;
454
455         if (argc != 2) {
456                 com_err(argv[0], 0, "Usage: setb <block>");
457                 return;
458         }
459         if (check_fs_open(argv[0]))
460                 return;
461         if (!(fs->flags & EXT2_FLAG_RW)) {
462                 com_err(argv[0], 0, "Filesystem opened read/only");
463                 return;
464         }
465         block = strtoul(argv[1], &tmp, 0);
466         if (!block || *tmp) {
467                 com_err(argv[0], 0, "No block 0");
468                 return;
469         } 
470         if (ext2fs_test_block_bitmap(fs->block_map,block))
471                 com_err(argv[0], 0, "Warning: block already set");
472         ext2fs_mark_block_bitmap(fs->block_map,block);
473         ext2fs_mark_bb_dirty(fs);
474 }
475
476 void do_testb(int argc, char *argv[])
477 {
478         blk_t block;
479         char *tmp;
480
481         if (argc != 2) {
482                 com_err(argv[0], 0, "Usage: testb <block>");
483                 return;
484         }
485         if (check_fs_open(argv[0]))
486                 return;
487         block = strtoul(argv[1], &tmp, 0);
488         if (!block || *tmp) {
489                 com_err(argv[0], 0, "No block 0");
490                 return;
491         } 
492         if (ext2fs_test_block_bitmap(fs->block_map,block))
493                 printf("Block %ld marked in use\n", block);
494         else printf("Block %ld not in use\n", block);
495 }
496
497 void modify_char(char *com, char *prompt, char *format, u_char *val)
498 {
499         char buf[200];
500         u_char v;
501         char *tmp;
502
503         sprintf(buf, format, *val);
504         printf("%30s    [%s] ", prompt, buf);
505         fgets(buf, sizeof(buf), stdin);
506         if (buf[strlen (buf) - 1] == '\n')
507                 buf[strlen (buf) - 1] = '\0';
508         if (!buf[0])
509                 return;
510         v = strtol(buf, &tmp, 0);
511         if (*tmp)
512                 com_err(com, 0, "Bad value - %s", buf);
513         else
514                 *val = v;
515 }
516
517 void modify_short(char *com, char *prompt, char *format, u_short *val)
518 {
519         char buf[200];
520         u_short v;
521         char *tmp;
522
523         sprintf(buf, format, *val);
524         printf("%30s    [%s] ", prompt, buf);
525         fgets(buf, sizeof(buf), stdin);
526         if (buf[strlen (buf) - 1] == '\n')
527                 buf[strlen (buf) - 1] = '\0';
528         if (!buf[0])
529                 return;
530         v = strtol(buf, &tmp, 0);
531         if (*tmp)
532                 com_err(com, 0, "Bad value - %s", buf);
533         else
534                 *val = v;
535 }
536
537 void modify_long(char *com, char *prompt, char *format, u_long *val)
538 {
539         char buf[200];
540         u_long v;
541         char *tmp;
542
543         sprintf(buf, format, *val);
544         printf("%30s    [%s] ", prompt, buf);
545         fgets(buf, sizeof(buf), stdin);
546         if (buf[strlen (buf) - 1] == '\n')
547                 buf[strlen (buf) - 1] = '\0';
548         if (!buf[0])
549                 return;
550         v = strtol(buf, &tmp, 0);
551         if (*tmp)
552                 com_err(com, 0, "Bad value - %s", buf);
553         else
554                 *val = v;
555 }
556
557
558 void do_modify_inode(int argc, char *argv[])
559 {
560         struct ext2_inode inode;
561         ino_t inode_num;
562         int i;
563         errcode_t       retval;
564         char    buf[80];
565         char *hex_format = "0x%x";
566         char *octal_format = "0%o";
567         char *decimal_format = "%d";
568         
569         if (argc != 2) {
570                 com_err(argv[0], 0, "Usage: modify_inode <file>");
571                 return;
572         }
573         if (check_fs_open(argv[0]))
574                 return;
575         if (!(fs->flags & EXT2_FLAG_RW)) {
576                 com_err(argv[0], 0, "Filesystem opened read/only");
577                 return;
578         }
579
580         inode_num = string_to_inode(argv[1]);
581         if (!inode_num) 
582                 return;
583
584         retval = ext2fs_read_inode(fs, inode_num, &inode);
585         if (retval) {
586                 com_err(argv[1], retval, "while trying to read inode %d",
587                         inode_num);
588                 return;
589         }
590         
591         modify_short(argv[0], "Mode", octal_format, &inode.i_mode);
592         modify_short(argv[0], "User ID", decimal_format, &inode.i_uid);
593         modify_short(argv[0], "Group ID", decimal_format, &inode.i_gid);
594         modify_long(argv[0], "Size", decimal_format, &inode.i_size);
595         modify_long(argv[0], "Creation time", decimal_format, &inode.i_ctime);
596         modify_long(argv[0], "Modification time", decimal_format, &inode.i_mtime);
597         modify_long(argv[0], "Access time", decimal_format, &inode.i_atime);
598         modify_long(argv[0], "Deletion time", decimal_format, &inode.i_dtime);
599         modify_short(argv[0], "Link count", decimal_format, &inode.i_links_count);
600         modify_long(argv[0], "Block count", decimal_format, &inode.i_blocks);
601         modify_long(argv[0], "File flags", hex_format, &inode.i_flags);
602         modify_long(argv[0], "Reserved1", decimal_format, &inode.i_reserved1);
603         modify_long(argv[0], "File acl", decimal_format, &inode.i_file_acl);
604         modify_long(argv[0], "Directory acl", decimal_format, &inode.i_dir_acl);
605         modify_long(argv[0], "Fragment address", decimal_format, &inode.i_faddr);
606         modify_char(argv[0], "Fragment number", decimal_format, &inode.i_frag);
607         modify_char(argv[0], "Fragment size", decimal_format, &inode.i_fsize);
608         for (i=0;  i < EXT2_NDIR_BLOCKS; i++) {
609                 sprintf(buf, "Direct Block #%d", i);
610                 modify_long(argv[0], buf, decimal_format, &inode.i_block[i]);
611         }
612         modify_long(argv[0], "Indirect Block", decimal_format,
613                     &inode.i_block[EXT2_IND_BLOCK]);    
614         modify_long(argv[0], "Double Indirect Block", decimal_format,
615                     &inode.i_block[EXT2_DIND_BLOCK]);
616         modify_long(argv[0], "Triple Indirect Block", decimal_format,
617                     &inode.i_block[EXT2_TIND_BLOCK]);
618         retval = ext2fs_write_inode(fs, inode_num, &inode);
619         if (retval) {
620                 com_err(argv[1], retval, "while trying to write inode %d",
621                         inode_num);
622                 return;
623         }
624 }
625
626 /*
627  * list directory
628  */
629
630 struct list_dir_struct {
631         FILE    *f;
632         int     col;
633 };
634
635 int list_dir_proc(struct ext2_dir_entry *dirent,
636                   int   offset,
637                   int   blocksize,
638                   char  *buf,
639                   void  *private)
640 {
641         char    name[EXT2_NAME_LEN];
642         char    tmp[EXT2_NAME_LEN + 16];
643
644         struct list_dir_struct *ls = (struct list_dir_struct *) private;
645         int     thislen;
646
647         thislen = (dirent->name_len < EXT2_NAME_LEN) ? dirent->name_len :
648                 EXT2_NAME_LEN;
649         strncpy(name, dirent->name, thislen);
650         name[thislen] = '\0';
651
652         sprintf(tmp, "%ld (%d) %s   ", dirent->inode, dirent->rec_len, name);
653         thislen = strlen(tmp);
654
655         if (ls->col + thislen > 80) {
656                 fprintf(ls->f, "\n");
657                 ls->col = 0;
658         }
659         fprintf(ls->f, "%s", tmp);
660         ls->col += thislen;
661                 
662         return 0;
663 }
664
665 void do_list_dir(int argc, char *argv[])
666 {
667         ino_t   inode;
668         int     retval;
669         struct list_dir_struct ls;
670         
671         if (argc > 2) {
672                 com_err(argv[0], 0, "Usage: list_dir [pathname]");
673                 return;
674         }
675         if (check_fs_open(argv[0]))
676                 return;
677
678         if (argc == 2)
679                 inode = string_to_inode(argv[1]);
680         else
681                 inode = cwd;
682         if (!inode)
683                 return;
684
685         ls.f = open_pager();
686         ls.col = 0;
687         retval = ext2fs_dir_iterate(fs, inode, DIRENT_FLAG_INCLUDE_EMPTY,
688                                     0, list_dir_proc, &ls);
689         fprintf(ls.f, "\n");
690         close_pager(ls.f);
691         if (retval)
692                 com_err(argv[1], retval, "");
693
694         return;
695 }
696
697 void do_change_working_dir(int argc, char *argv[])
698 {
699         ino_t   inode;
700         int     retval;
701         
702         if (argc != 2) {
703                 com_err(argv[0], 0, "Usage: cd <file>");
704                 return;
705         }
706         if (check_fs_open(argv[0]))
707                 return;
708
709         inode = string_to_inode(argv[1]);
710         if (!inode) 
711                 return;
712
713         retval = ext2fs_check_directory(fs, inode);
714         if (retval) {
715                 com_err(argv[1], retval, "");
716                 return;
717         }
718         cwd = inode;
719         return;
720 }
721
722 void do_print_working_directory(int argc, char *argv[])
723 {
724         int     retval;
725         char    *pathname = NULL;
726         
727         if (argc > 1) {
728                 com_err(argv[0], 0, "Usage: print_working_directory");
729                 return;
730         }
731         if (check_fs_open(argv[0]))
732                 return;
733
734         retval = ext2fs_get_pathname(fs, cwd, 0, &pathname);
735         if (retval) {
736                 com_err(argv[0], retval,
737                         "while trying to get pathname of cwd");
738         }
739         printf("[pwd]   INODE: %6ld  PATH: %s\n", cwd, pathname);
740         free(pathname);
741         retval = ext2fs_get_pathname(fs, root, 0, &pathname);
742         if (retval) {
743                 com_err(argv[0], retval,
744                         "while trying to get pathname of root");
745         }
746         printf("[root]  INODE: %6ld  PATH: %s\n", root, pathname);
747         free(pathname);
748         return;
749 }
750
751
752 void make_link(char *sourcename, char *destname)
753 {
754         ino_t   inode;
755         int     retval;
756         ino_t   dir;
757         char    *dest, *cp, *basename;
758
759         /*
760          * Get the source inode
761          */
762         inode = string_to_inode(sourcename);
763         if (!inode)
764                 return;
765         basename = strrchr(sourcename, '/');
766         if (basename)
767                 basename++;
768         else
769                 basename = sourcename;
770         /*
771          * Figure out the destination.  First see if it exists and is
772          * a directory.  
773          */
774         if (! (retval=ext2fs_namei(fs, root, cwd, destname, &dir)))
775                 dest = basename;
776         else {
777                 /*
778                  * OK, it doesn't exist.  See if it is
779                  * '<dir>/basename' or 'basename'
780                  */
781                 cp = strrchr(destname, '/');
782                 if (cp) {
783                         *cp = 0;
784                         dir = string_to_inode(destname);
785                         if (!dir)
786                                 return;
787                         dest = cp+1;
788                 } else {
789                         dir = cwd;
790                         dest = destname;
791                 }
792         }
793         
794         retval = ext2fs_link(fs, dir, dest, inode, 0);
795         if (retval)
796                 com_err("make_link", retval, "");
797         return;
798 }
799
800
801 void do_link(int argc, char *argv[])
802 {
803         if (argc != 3) {
804                 com_err(argv[0], 0, "Usage: link <source_file> <dest_name>");
805                 return;
806         }
807         if (check_fs_open(argv[0]))
808                 return;
809
810         make_link(argv[1], argv[2]);
811 }
812
813
814 void unlink_file_by_name(char *filename)
815 {
816         int     retval;
817         ino_t   dir;
818         char    *basename;
819         
820         basename = strrchr(filename, '/');
821         if (basename) {
822                 *basename++ = '0';
823                 dir = string_to_inode(filename);
824                 if (!dir)
825                         return;
826         } else {
827                 dir = cwd;
828                 basename = filename;
829         }
830         retval = ext2fs_unlink(fs, dir, basename, 0, 0);
831         if (retval)
832                 com_err("unlink_file_by_name", retval, "");
833         return;
834 }
835
836 void do_unlink(int argc, char *argv[])
837 {
838         if (argc != 2) {
839                 com_err(argv[0], 0, "Usage: unlink <pathname>");
840                 return;
841         }
842         if (check_fs_open(argv[0]))
843                 return;
844
845         unlink_file_by_name(argv[1]);
846 }
847
848 void do_find_free_block(int argc, char *argv[])
849 {
850         blk_t   free_blk, goal;
851         errcode_t       retval;
852         char            *tmp;
853         
854         if (argc > 2 || *argv[1] == '?') {
855                 com_err(argv[0], 0, "Usage: find_free_block <goal>");
856                 return;
857         }
858         if (check_fs_open(argv[0]))
859                 return;
860
861         if (argc > 1) {
862                 goal = strtol(argv[1], &tmp, 0);
863                 if (*tmp) {
864                         com_err(argv[0], 0, "Bad goal - %s", argv[1]);
865                         return;
866                 }
867         }
868         else
869                 goal = fs->super->s_first_data_block;
870
871         retval = ext2fs_new_block(fs, goal, 0, &free_blk);
872         if (retval)
873                 com_err("ext2fs_new_block", retval, "");
874         else
875                 printf("Free block found: %ld\n", free_blk);
876
877 }
878
879 void do_find_free_inode(int argc, char *argv[])
880 {
881         ino_t   free_inode, dir;
882         int     mode;
883         int     retval;
884         char    *tmp;
885         
886         if (argc > 3 || *argv[1] == '?') {
887                 com_err(argv[0], 0, "Usage: find_free_inode <dir> <mode>");
888                 return;
889         }
890         if (check_fs_open(argv[0]))
891                 return;
892
893         if (argc > 1) {
894                 dir = strtol(argv[1], &tmp, 0);
895                 if (*tmp) {
896                         com_err(argv[0], 0, "Bad dir - %s", argv[1]);
897                         return;
898                 }
899         }
900         else
901                 dir = root;
902         if (argc > 2) {
903                 mode = strtol(argv[2], &tmp, 0);
904                 if (*tmp) {
905                         com_err(argv[0], 0, "Bad mode - %s", argv[2]);
906                         return;
907                 }
908         }
909         else
910                 mode = 010755;
911
912         retval = ext2fs_new_inode(fs, dir, mode, 0, &free_inode);
913         if (retval)
914                 com_err("ext2fs_new_inode", retval, "");
915         else
916                 printf("Free inode found: %ld\n", free_inode);
917 }
918
919 void do_mkdir(int argc, char *argv[])
920 {
921         char    *cp;
922         ino_t   parent;
923         char    *name;
924         errcode_t retval;
925
926         if (check_fs_open(argv[0]))
927                 return;
928
929         if (argc != 2) {
930                 com_err(argv[0], 0, "Usage: mkdir <file>");
931                 return;
932         }
933
934         cp = strrchr(argv[1], '/');
935         if (cp) {
936                 *cp = 0;
937                 parent = string_to_inode(argv[1]);
938                 if (!parent) {
939                         com_err(argv[1], ENOENT, "");
940                         return;
941                 }
942                 name = cp+1;
943         } else {
944                 parent = cwd;
945                 name = argv[1];
946         }
947
948
949         retval = ext2fs_mkdir(fs, parent, 0, name);
950         if (retval) {
951                 com_err("ext2fs_mkdir", retval, "");
952                 return;
953         }
954
955 }
956
957 void do_rmdir(int argc, char *argv[])
958 {
959         printf("Unimplemented\n");
960 }
961
962
963 int release_blocks_proc(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
964 {
965         printf("%ld ", *blocknr);
966         ext2fs_unmark_block_bitmap(fs->block_map,*blocknr);
967         return 0;
968 }
969
970 void kill_file_by_inode(ino_t inode)
971 {
972         struct ext2_inode inode_buf;
973
974         ext2fs_read_inode(fs, inode, &inode_buf);
975         inode_buf.i_dtime = time(NULL);
976         ext2fs_write_inode(fs, inode, &inode_buf);
977
978         printf("Kill file by inode %ld\n", inode);
979         ext2fs_block_iterate(fs,inode,0,NULL,release_blocks_proc,NULL);
980         ext2fs_unmark_inode_bitmap(fs->inode_map,inode);
981
982         ext2fs_mark_bb_dirty(fs);
983         ext2fs_mark_ib_dirty(fs);
984 }
985
986
987 void do_kill_file(int argc, char *argv[])
988 {
989         ino_t inode_num;
990
991         if (argc != 2) {
992                 com_err(argv[0], 0, "Usage: kill_file <file>");
993                 return;
994         }
995         if (check_fs_open(argv[0]))
996                 return;
997
998         inode_num = string_to_inode(argv[1]);
999         if (!inode_num) {
1000                 com_err(argv[0], 0, "Cannot find file");
1001                 return;
1002         }
1003         kill_file_by_inode(inode_num);
1004 }
1005
1006 void do_rm(int argc, char *argv[])
1007 {
1008         int retval;
1009         ino_t inode_num;
1010         struct ext2_inode inode;
1011
1012         if (argc != 2) {
1013                 com_err(argv[0], 0, "Usage: rm <filename>");
1014                 return;
1015         }
1016         if (check_fs_open(argv[0]))
1017                 return;
1018
1019         retval = ext2fs_namei(fs, root, cwd, argv[1], &inode_num);
1020         if (retval) {
1021                 com_err(argv[0], 0, "Cannot find file");
1022                 return;
1023         }
1024
1025         retval = ext2fs_read_inode(fs,inode_num,&inode);
1026         if (retval) {
1027                 com_err(argv[0], retval, "while reading file's inode");
1028                 return;
1029         }
1030
1031         if (S_ISDIR(inode.i_mode)) {
1032                 com_err(argv[0], 0, "file is a directory");
1033                 return;
1034         }
1035
1036         --inode.i_links_count;
1037         retval = ext2fs_write_inode(fs,inode_num,&inode);
1038         if (retval) {
1039                 com_err(argv[0], retval, "while writing inode");
1040                 return;
1041         }
1042
1043         unlink_file_by_name(argv[1]);
1044         if (inode.i_links_count == 0)
1045                 kill_file_by_inode(inode_num);
1046 }
1047
1048 void do_show_debugfs_params(int argc, char *argv[])
1049 {
1050         FILE *out = stdout;
1051
1052         fprintf(out, "Open mode: read-%s\n",
1053                 fs->flags & EXT2_FLAG_RW ? "write" : "only");
1054         fprintf(out, "Filesystem in use: %s\n",
1055                 fs ? fs->device_name : "--none--");
1056 }
1057
1058 void do_expand_dir(int argc, char *argv[])
1059 {
1060         ino_t inode;
1061         int retval;
1062
1063         if (argc != 2) {
1064                 com_err(argv[0], 0, "Usage: expand_dir <file>");
1065                 return;
1066         }
1067         if (check_fs_open(argv[0]))
1068                 return;
1069         inode = string_to_inode(argv[1]);
1070         if (!inode)
1071                 return;
1072
1073         retval = ext2fs_expand_dir(fs, inode);
1074         if (retval)
1075                 com_err("ext2fs_expand_dir", retval, "");
1076         return;
1077 }
1078
1079 void main(int argc, char **argv)
1080 {
1081         int     retval;
1082         int     sci_idx;
1083         char    *usage = "Usage: debugfs [[-w] device]";
1084         char    c;
1085         int open_flags = 0;
1086         
1087         initialize_ext2_error_table();
1088
1089         while ((c = getopt (argc, argv, "w")) != EOF) {
1090                 switch (c) {
1091                 case 'w':
1092                         open_flags = EXT2_FLAG_RW;
1093                         break;
1094                 default:
1095                         com_err(argv[0], 0, usage);
1096                         return;
1097                 }
1098         }
1099         if (optind < argc)
1100                 open_filesystem(argv[optind], open_flags);
1101         
1102         sci_idx = ss_create_invocation("debugfs", "0.0", (char *) NULL,
1103                                        &debug_cmds, &retval);
1104         if (retval) {
1105                 ss_perror(sci_idx, retval, "creating invocation");
1106                 exit(1);
1107         }
1108
1109         (void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &retval);
1110         if (retval) {
1111                 ss_perror(sci_idx, retval, "adding standard requests");
1112                 exit (1);
1113         }
1114
1115         ss_listen(sci_idx);
1116
1117         if (fs)
1118                 close_filesystem();
1119         
1120         exit(0);
1121 }
1122