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