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