debugfs.c revision 73b054259632148e789f61cce244ec4fa929721d
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
40extern ss_request_table debug_cmds;
41ss_request_table *extra_cmds;
42const char *debug_prog_name;
43
44ext2_filsys	current_fs = NULL;
45ext2_ino_t	root, cwd;
46
47static void open_filesystem(char *device, int open_flags, blk_t superblock,
48			    blk_t blocksize, int catastrophic,
49			    char *data_filename)
50{
51	int	retval;
52	io_channel data_io = 0;
53
54	if (superblock != 0 && blocksize == 0) {
55		com_err(device, 0, "if you specify the superblock, you must also specify the block size");
56		current_fs = NULL;
57		return;
58	}
59
60	if (data_filename) {
61		if ((open_flags & EXT2_FLAG_IMAGE_FILE) == 0) {
62			com_err(device, 0,
63				"The -d option is only valid when reading an e2image file");
64			current_fs = NULL;
65			return;
66		}
67		retval = unix_io_manager->open(data_filename, 0, &data_io);
68		if (retval) {
69			com_err(data_filename, 0, "while opening data source");
70			current_fs = NULL;
71			return;
72		}
73	}
74
75	if (catastrophic && (open_flags & EXT2_FLAG_RW)) {
76		com_err(device, 0,
77			"opening read-only because of catastrophic mode");
78		open_flags &= ~EXT2_FLAG_RW;
79	}
80
81	retval = ext2fs_open(device, open_flags, superblock, blocksize,
82			     unix_io_manager, &current_fs);
83	if (retval) {
84		com_err(device, retval, "while opening filesystem");
85		current_fs = NULL;
86		return;
87	}
88
89	if (catastrophic)
90		com_err(device, 0, "catastrophic mode - not reading inode or group bitmaps");
91	else {
92		retval = ext2fs_read_inode_bitmap(current_fs);
93		if (retval) {
94			com_err(device, retval, "while reading inode bitmap");
95			goto errout;
96		}
97		retval = ext2fs_read_block_bitmap(current_fs);
98		if (retval) {
99			com_err(device, retval, "while reading block bitmap");
100			goto errout;
101		}
102	}
103
104	if (data_io) {
105		retval = ext2fs_set_data_io(current_fs, data_io);
106		if (retval) {
107			com_err(device, retval,
108				"while setting data source");
109			goto errout;
110		}
111	}
112
113	root = cwd = EXT2_ROOT_INO;
114	return;
115
116errout:
117	retval = ext2fs_close(current_fs);
118	if (retval)
119		com_err(device, retval, "while trying to close filesystem");
120	current_fs = NULL;
121}
122
123void do_open_filesys(int argc, char **argv)
124{
125	int	c, err;
126	int	catastrophic = 0;
127	blk_t	superblock = 0;
128	blk_t	blocksize = 0;
129	int	open_flags = EXT2_FLAG_SOFTSUPP_FEATURES;
130	char	*data_filename = 0;
131
132	reset_getopt();
133	while ((c = getopt (argc, argv, "iwfecb:s:d:")) != EOF) {
134		switch (c) {
135		case 'i':
136			open_flags |= EXT2_FLAG_IMAGE_FILE;
137			break;
138		case 'w':
139			open_flags |= EXT2_FLAG_RW;
140			break;
141		case 'f':
142			open_flags |= EXT2_FLAG_FORCE;
143			break;
144		case 'e':
145			open_flags |= EXT2_FLAG_EXCLUSIVE;
146			break;
147		case 'c':
148			catastrophic = 1;
149			break;
150		case 'd':
151			data_filename = optarg;
152			break;
153		case 'b':
154			blocksize = parse_ulong(optarg, argv[0],
155						"block size", &err);
156			if (err)
157				return;
158			break;
159		case 's':
160			superblock = parse_ulong(optarg, argv[0],
161						 "superblock number", &err);
162			if (err)
163				return;
164			break;
165		default:
166			goto print_usage;
167		}
168	}
169	if (optind != argc-1) {
170		goto print_usage;
171	}
172	if (check_fs_not_open(argv[0]))
173		return;
174	open_filesystem(argv[optind], open_flags,
175			superblock, blocksize, catastrophic,
176			data_filename);
177	return;
178
179print_usage:
180	fprintf(stderr, "%s: Usage: open [-s superblock] [-b blocksize] "
181		"[-c] [-w] <device>\n", argv[0]);
182}
183
184void do_lcd(int argc, char **argv)
185{
186	if (argc != 2) {
187		com_err(argv[0], 0, "Usage: %s %s", argv[0], "<native dir>");
188		return;
189	}
190
191	if (chdir(argv[1]) == -1) {
192		com_err(argv[0], errno,
193			"while trying to change native directory to %s",
194			argv[1]);
195		return;
196	}
197}
198
199static void close_filesystem(NOARGS)
200{
201	int	retval;
202
203	if (current_fs->flags & EXT2_FLAG_IB_DIRTY) {
204		retval = ext2fs_write_inode_bitmap(current_fs);
205		if (retval)
206			com_err("ext2fs_write_inode_bitmap", retval, 0);
207	}
208	if (current_fs->flags & EXT2_FLAG_BB_DIRTY) {
209		retval = ext2fs_write_block_bitmap(current_fs);
210		if (retval)
211			com_err("ext2fs_write_block_bitmap", retval, 0);
212	}
213	retval = ext2fs_close(current_fs);
214	if (retval)
215		com_err("ext2fs_close", retval, 0);
216	current_fs = NULL;
217	return;
218}
219
220void do_close_filesys(int argc, char **argv)
221{
222	if (common_args_process(argc, argv, 1, 1, "close_filesys", "", 0))
223		return;
224	close_filesystem();
225}
226
227void do_init_filesys(int argc, char **argv)
228{
229	struct ext2_super_block param;
230	errcode_t	retval;
231	int		err;
232
233	if (common_args_process(argc, argv, 3, 3, "initialize",
234				"<device> <blocksize>", CHECK_FS_NOTOPEN))
235		return;
236
237	memset(&param, 0, sizeof(struct ext2_super_block));
238	param.s_blocks_count = parse_ulong(argv[2], argv[0],
239					   "blocks count", &err);
240	if (err)
241		return;
242	retval = ext2fs_initialize(argv[1], 0, &param,
243				   unix_io_manager, &current_fs);
244	if (retval) {
245		com_err(argv[1], retval, "while initializing filesystem");
246		current_fs = NULL;
247		return;
248	}
249	root = cwd = EXT2_ROOT_INO;
250	return;
251}
252
253static void print_features(struct ext2_super_block * s, FILE *f)
254{
255	int	i, j, printed=0;
256	__u32	*mask = &s->s_feature_compat, m;
257
258	fputs("Filesystem features:", f);
259	for (i=0; i <3; i++,mask++) {
260		for (j=0,m=1; j < 32; j++, m<<=1) {
261			if (*mask & m) {
262				fprintf(f, " %s", e2p_feature2string(i, m));
263				printed++;
264			}
265		}
266	}
267	if (printed == 0)
268		fputs("(none)", f);
269	fputs("\n", f);
270}
271
272static void print_bg_opts(struct ext2_group_desc *gdp, int mask,
273			  const char *str, int *first, FILE *f)
274{
275	if (gdp->bg_flags & mask) {
276		if (*first) {
277			fputs("           [", f);
278			*first = 0;
279		} else
280			fputs(", ", f);
281		fputs(str, f);
282	}
283}
284
285void do_show_super_stats(int argc, char *argv[])
286{
287	dgrp_t	i;
288	FILE 	*out;
289	struct ext2_group_desc *gdp;
290	int	c, header_only = 0;
291	int	numdirs = 0, first, gdt_csum;
292
293	gdt_csum = EXT2_HAS_RO_COMPAT_FEATURE(current_fs->super,
294					      EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
295
296	reset_getopt();
297	while ((c = getopt (argc, argv, "h")) != EOF) {
298		switch (c) {
299		case 'h':
300			header_only++;
301			break;
302		default:
303			goto print_usage;
304		}
305	}
306	if (optind != argc) {
307		goto print_usage;
308	}
309	if (check_fs_open(argv[0]))
310		return;
311	out = open_pager();
312
313	list_super2(current_fs->super, out);
314	for (i=0; i < current_fs->group_desc_count; i++)
315		numdirs += current_fs->group_desc[i].bg_used_dirs_count;
316	fprintf(out, "Directories:              %d\n", numdirs);
317
318	if (header_only) {
319		close_pager(out);
320		return;
321	}
322
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 (debugfs_write_new_inode(newfile, &inode, argv[0])) {
1390		close(fd);
1391		return;
1392	}
1393	if (LINUX_S_ISREG(inode.i_mode)) {
1394		retval = copy_file(fd, newfile);
1395		if (retval)
1396			com_err("copy_file", retval, 0);
1397	}
1398	close(fd);
1399}
1400
1401void do_mknod(int argc, char *argv[])
1402{
1403	unsigned long	mode, major, minor;
1404	ext2_ino_t	newfile;
1405	errcode_t 	retval;
1406	struct ext2_inode inode;
1407	int		filetype, nr;
1408
1409	if (check_fs_open(argv[0]))
1410		return;
1411	if (argc < 3 || argv[2][1]) {
1412	usage:
1413		com_err(argv[0], 0, "Usage: mknod <name> [p| [c|b] <major> <minor>]");
1414		return;
1415	}
1416	mode = minor = major = 0;
1417	switch (argv[2][0]) {
1418		case 'p':
1419			mode = LINUX_S_IFIFO;
1420			filetype = EXT2_FT_FIFO;
1421			nr = 3;
1422			break;
1423		case 'c':
1424			mode = LINUX_S_IFCHR;
1425			filetype = EXT2_FT_CHRDEV;
1426			nr = 5;
1427			break;
1428		case 'b':
1429			mode = LINUX_S_IFBLK;
1430			filetype = EXT2_FT_BLKDEV;
1431			nr = 5;
1432			break;
1433		default:
1434			filetype = 0;
1435			nr = 0;
1436	}
1437	if (nr == 5) {
1438		major = strtoul(argv[3], argv+3, 0);
1439		minor = strtoul(argv[4], argv+4, 0);
1440		if (major > 65535 || minor > 65535 || argv[3][0] || argv[4][0])
1441			nr = 0;
1442	}
1443	if (argc != nr)
1444		goto usage;
1445	if (check_fs_read_write(argv[0]))
1446		return;
1447	retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
1448	if (retval) {
1449		com_err(argv[0], retval, 0);
1450		return;
1451	}
1452	printf("Allocated inode: %u\n", newfile);
1453	retval = ext2fs_link(current_fs, cwd, argv[1], newfile, filetype);
1454	if (retval == EXT2_ET_DIR_NO_SPACE) {
1455		retval = ext2fs_expand_dir(current_fs, cwd);
1456		if (retval) {
1457			com_err(argv[0], retval, "while expanding directory");
1458			return;
1459		}
1460		retval = ext2fs_link(current_fs, cwd, argv[1], newfile,
1461				     filetype);
1462	}
1463	if (retval) {
1464		com_err(argv[1], retval, 0);
1465		return;
1466	}
1467        if (ext2fs_test_inode_bitmap(current_fs->inode_map,newfile))
1468		com_err(argv[0], 0, "Warning: inode already set");
1469	ext2fs_mark_inode_bitmap(current_fs->inode_map, newfile);
1470	ext2fs_mark_ib_dirty(current_fs);
1471	memset(&inode, 0, sizeof(inode));
1472	inode.i_mode = mode;
1473	inode.i_atime = inode.i_ctime = inode.i_mtime =
1474		current_fs->now ? current_fs->now : time(0);
1475	if ((major < 256) && (minor < 256)) {
1476		inode.i_block[0] = major*256+minor;
1477		inode.i_block[1] = 0;
1478	} else {
1479		inode.i_block[0] = 0;
1480		inode.i_block[1] = (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
1481	}
1482	inode.i_links_count = 1;
1483	if (debugfs_write_new_inode(newfile, &inode, argv[0]))
1484		return;
1485}
1486
1487void do_mkdir(int argc, char *argv[])
1488{
1489	char	*cp;
1490	ext2_ino_t	parent;
1491	char	*name;
1492	errcode_t retval;
1493
1494	if (common_args_process(argc, argv, 2, 2, "mkdir",
1495				"<filename>", CHECK_FS_RW))
1496		return;
1497
1498	cp = strrchr(argv[1], '/');
1499	if (cp) {
1500		*cp = 0;
1501		parent = string_to_inode(argv[1]);
1502		if (!parent) {
1503			com_err(argv[1], ENOENT, 0);
1504			return;
1505		}
1506		name = cp+1;
1507	} else {
1508		parent = cwd;
1509		name = argv[1];
1510	}
1511
1512try_again:
1513	retval = ext2fs_mkdir(current_fs, parent, 0, name);
1514	if (retval == EXT2_ET_DIR_NO_SPACE) {
1515		retval = ext2fs_expand_dir(current_fs, parent);
1516		if (retval) {
1517			com_err(argv[0], retval, "while expanding directory");
1518			return;
1519		}
1520		goto try_again;
1521	}
1522	if (retval) {
1523		com_err("ext2fs_mkdir", retval, 0);
1524		return;
1525	}
1526
1527}
1528
1529static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
1530			       int blockcnt EXT2FS_ATTR((unused)),
1531			       void *private EXT2FS_ATTR((unused)))
1532{
1533	blk_t	block;
1534
1535	block = *blocknr;
1536	ext2fs_block_alloc_stats(fs, block, -1);
1537	return 0;
1538}
1539
1540static void kill_file_by_inode(ext2_ino_t inode)
1541{
1542	struct ext2_inode inode_buf;
1543
1544	if (debugfs_read_inode(inode, &inode_buf, 0))
1545		return;
1546	inode_buf.i_dtime = current_fs->now ? current_fs->now : time(0);
1547	if (debugfs_write_inode(inode, &inode_buf, 0))
1548		return;
1549	if (!ext2fs_inode_has_valid_blocks(&inode_buf))
1550		return;
1551
1552	ext2fs_block_iterate(current_fs, inode, BLOCK_FLAG_READ_ONLY, NULL,
1553			     release_blocks_proc, NULL);
1554	printf("\n");
1555	ext2fs_inode_alloc_stats2(current_fs, inode, -1,
1556				  LINUX_S_ISDIR(inode_buf.i_mode));
1557}
1558
1559
1560void do_kill_file(int argc, char *argv[])
1561{
1562	ext2_ino_t inode_num;
1563
1564	if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
1565		return;
1566
1567	kill_file_by_inode(inode_num);
1568}
1569
1570void do_rm(int argc, char *argv[])
1571{
1572	int retval;
1573	ext2_ino_t inode_num;
1574	struct ext2_inode inode;
1575
1576	if (common_args_process(argc, argv, 2, 2, "rm",
1577				"<filename>", CHECK_FS_RW))
1578		return;
1579
1580	retval = ext2fs_namei(current_fs, root, cwd, argv[1], &inode_num);
1581	if (retval) {
1582		com_err(argv[0], retval, "while trying to resolve filename");
1583		return;
1584	}
1585
1586	if (debugfs_read_inode(inode_num, &inode, argv[0]))
1587		return;
1588
1589	if (LINUX_S_ISDIR(inode.i_mode)) {
1590		com_err(argv[0], 0, "file is a directory");
1591		return;
1592	}
1593
1594	--inode.i_links_count;
1595	if (debugfs_write_inode(inode_num, &inode, argv[0]))
1596		return;
1597
1598	unlink_file_by_name(argv[1]);
1599	if (inode.i_links_count == 0)
1600		kill_file_by_inode(inode_num);
1601}
1602
1603struct rd_struct {
1604	ext2_ino_t	parent;
1605	int		empty;
1606};
1607
1608static int rmdir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)),
1609		      int	entry EXT2FS_ATTR((unused)),
1610		      struct ext2_dir_entry *dirent,
1611		      int	offset EXT2FS_ATTR((unused)),
1612		      int	blocksize EXT2FS_ATTR((unused)),
1613		      char	*buf EXT2FS_ATTR((unused)),
1614		      void	*private)
1615{
1616	struct rd_struct *rds = (struct rd_struct *) private;
1617
1618	if (dirent->inode == 0)
1619		return 0;
1620	if (((dirent->name_len&0xFF) == 1) && (dirent->name[0] == '.'))
1621		return 0;
1622	if (((dirent->name_len&0xFF) == 2) && (dirent->name[0] == '.') &&
1623	    (dirent->name[1] == '.')) {
1624		rds->parent = dirent->inode;
1625		return 0;
1626	}
1627	rds->empty = 0;
1628	return 0;
1629}
1630
1631void do_rmdir(int argc, char *argv[])
1632{
1633	int retval;
1634	ext2_ino_t inode_num;
1635	struct ext2_inode inode;
1636	struct rd_struct rds;
1637
1638	if (common_args_process(argc, argv, 2, 2, "rmdir",
1639				"<filename>", CHECK_FS_RW))
1640		return;
1641
1642	retval = ext2fs_namei(current_fs, root, cwd, argv[1], &inode_num);
1643	if (retval) {
1644		com_err(argv[0], retval, "while trying to resolve filename");
1645		return;
1646	}
1647
1648	if (debugfs_read_inode(inode_num, &inode, argv[0]))
1649		return;
1650
1651	if (!LINUX_S_ISDIR(inode.i_mode)) {
1652		com_err(argv[0], 0, "file is not a directory");
1653		return;
1654	}
1655
1656	rds.parent = 0;
1657	rds.empty = 1;
1658
1659	retval = ext2fs_dir_iterate2(current_fs, inode_num, 0,
1660				    0, rmdir_proc, &rds);
1661	if (retval) {
1662		com_err(argv[0], retval, "while iterating over directory");
1663		return;
1664	}
1665	if (rds.empty == 0) {
1666		com_err(argv[0], 0, "directory not empty");
1667		return;
1668	}
1669
1670	inode.i_links_count = 0;
1671	if (debugfs_write_inode(inode_num, &inode, argv[0]))
1672		return;
1673
1674	unlink_file_by_name(argv[1]);
1675	kill_file_by_inode(inode_num);
1676
1677	if (rds.parent) {
1678		if (debugfs_read_inode(rds.parent, &inode, argv[0]))
1679			return;
1680		if (inode.i_links_count > 1)
1681			inode.i_links_count--;
1682		if (debugfs_write_inode(rds.parent, &inode, argv[0]))
1683			return;
1684	}
1685}
1686
1687void do_show_debugfs_params(int argc EXT2FS_ATTR((unused)),
1688			    char *argv[] EXT2FS_ATTR((unused)))
1689{
1690	FILE *out = stdout;
1691
1692	if (current_fs)
1693		fprintf(out, "Open mode: read-%s\n",
1694			current_fs->flags & EXT2_FLAG_RW ? "write" : "only");
1695	fprintf(out, "Filesystem in use: %s\n",
1696		current_fs ? current_fs->device_name : "--none--");
1697}
1698
1699void do_expand_dir(int argc, char *argv[])
1700{
1701	ext2_ino_t inode;
1702	int retval;
1703
1704	if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
1705		return;
1706
1707	retval = ext2fs_expand_dir(current_fs, inode);
1708	if (retval)
1709		com_err("ext2fs_expand_dir", retval, 0);
1710	return;
1711}
1712
1713void do_features(int argc, char *argv[])
1714{
1715	int	i;
1716
1717	if (check_fs_open(argv[0]))
1718		return;
1719
1720	if ((argc != 1) && check_fs_read_write(argv[0]))
1721		return;
1722	for (i=1; i < argc; i++) {
1723		if (e2p_edit_feature(argv[i],
1724				     &current_fs->super->s_feature_compat, 0))
1725			com_err(argv[0], 0, "Unknown feature: %s\n",
1726				argv[i]);
1727		else
1728			ext2fs_mark_super_dirty(current_fs);
1729	}
1730	print_features(current_fs->super, stdout);
1731}
1732
1733void do_bmap(int argc, char *argv[])
1734{
1735	ext2_ino_t	ino;
1736	blk_t		blk, pblk;
1737	int		err;
1738	errcode_t	errcode;
1739
1740	if (common_args_process(argc, argv, 3, 3, argv[0],
1741				"<file> logical_blk", 0))
1742		return;
1743
1744	ino = string_to_inode(argv[1]);
1745	if (!ino)
1746		return;
1747	blk = parse_ulong(argv[2], argv[0], "logical_block", &err);
1748
1749	errcode = ext2fs_bmap(current_fs, ino, 0, 0, 0, blk, &pblk);
1750	if (errcode) {
1751		com_err("argv[0]", errcode,
1752			"while mapping logical block %u\n", blk);
1753		return;
1754	}
1755	printf("%u\n", pblk);
1756}
1757
1758void do_imap(int argc, char *argv[])
1759{
1760	ext2_ino_t	ino;
1761	unsigned long 	group, block, block_nr, offset;
1762
1763	if (common_args_process(argc, argv, 2, 2, argv[0],
1764				"<file>", 0))
1765		return;
1766	ino = string_to_inode(argv[1]);
1767	if (!ino)
1768		return;
1769
1770	group = (ino - 1) / EXT2_INODES_PER_GROUP(current_fs->super);
1771	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(current_fs->super)) *
1772		EXT2_INODE_SIZE(current_fs->super);
1773	block = offset >> EXT2_BLOCK_SIZE_BITS(current_fs->super);
1774	if (!current_fs->group_desc[(unsigned)group].bg_inode_table) {
1775		com_err(argv[0], 0, "Inode table for group %lu is missing\n",
1776			group);
1777		return;
1778	}
1779	block_nr = current_fs->group_desc[(unsigned)group].bg_inode_table +
1780		block;
1781	offset &= (EXT2_BLOCK_SIZE(current_fs->super) - 1);
1782
1783	printf("Inode %d is part of block group %lu\n"
1784	       "\tlocated at block %lu, offset 0x%04lx\n", ino, group,
1785	       block_nr, offset);
1786
1787}
1788
1789void do_set_current_time(int argc, char *argv[])
1790{
1791	time_t now;
1792
1793	if (common_args_process(argc, argv, 2, 2, argv[0],
1794				"<time>", 0))
1795		return;
1796
1797	now = string_to_time(argv[1]);
1798	if (now == ((time_t) -1)) {
1799		com_err(argv[0], 0, "Couldn't parse argument as a time: %s\n",
1800			argv[1]);
1801		return;
1802
1803	} else {
1804		printf("Setting current time to %s\n", time_to_string(now));
1805		current_fs->now = now;
1806	}
1807}
1808
1809static int source_file(const char *cmd_file, int sci_idx)
1810{
1811	FILE		*f;
1812	char		buf[256];
1813	char		*cp;
1814	int		exit_status = 0;
1815	int		retval;
1816
1817	if (strcmp(cmd_file, "-") == 0)
1818		f = stdin;
1819	else {
1820		f = fopen(cmd_file, "r");
1821		if (!f) {
1822			perror(cmd_file);
1823			exit(1);
1824		}
1825	}
1826	fflush(stdout);
1827	fflush(stderr);
1828	setbuf(stdout, NULL);
1829	setbuf(stderr, NULL);
1830	while (!feof(f)) {
1831		if (fgets(buf, sizeof(buf), f) == NULL)
1832			break;
1833		cp = strchr(buf, '\n');
1834		if (cp)
1835			*cp = 0;
1836		cp = strchr(buf, '\r');
1837		if (cp)
1838			*cp = 0;
1839		printf("debugfs: %s\n", buf);
1840		retval = ss_execute_line(sci_idx, buf);
1841		if (retval) {
1842			ss_perror(sci_idx, retval, buf);
1843			exit_status++;
1844		}
1845	}
1846	return exit_status;
1847}
1848
1849int main(int argc, char **argv)
1850{
1851	int		retval;
1852	int		sci_idx;
1853	const char	*usage = "Usage: %s [-b blocksize] [-s superblock] [-f cmd_file] [-R request] [-V] [[-w] [-c] device]";
1854	int		c;
1855	int		open_flags = EXT2_FLAG_SOFTSUPP_FEATURES;
1856	char		*request = 0;
1857	int		exit_status = 0;
1858	char		*cmd_file = 0;
1859	blk_t		superblock = 0;
1860	blk_t		blocksize = 0;
1861	int		catastrophic = 0;
1862	char		*data_filename = 0;
1863
1864	if (debug_prog_name == 0)
1865		debug_prog_name = "debugfs";
1866
1867	add_error_table(&et_ext2_error_table);
1868	fprintf (stderr, "%s %s (%s)\n", debug_prog_name,
1869		 E2FSPROGS_VERSION, E2FSPROGS_DATE);
1870
1871	while ((c = getopt (argc, argv, "iwcR:f:b:s:Vd:")) != EOF) {
1872		switch (c) {
1873		case 'R':
1874			request = optarg;
1875			break;
1876		case 'f':
1877			cmd_file = optarg;
1878			break;
1879		case 'd':
1880			data_filename = optarg;
1881			break;
1882		case 'i':
1883			open_flags |= EXT2_FLAG_IMAGE_FILE;
1884			break;
1885		case 'w':
1886			open_flags |= EXT2_FLAG_RW;
1887			break;
1888		case 'b':
1889			blocksize = parse_ulong(optarg, argv[0],
1890						"block size", 0);
1891			break;
1892		case 's':
1893			superblock = parse_ulong(optarg, argv[0],
1894						 "superblock number", 0);
1895			break;
1896		case 'c':
1897			catastrophic = 1;
1898			break;
1899		case 'V':
1900			/* Print version number and exit */
1901			fprintf(stderr, "\tUsing %s\n",
1902				error_message(EXT2_ET_BASE));
1903			exit(0);
1904		default:
1905			com_err(argv[0], 0, usage, debug_prog_name);
1906			return 1;
1907		}
1908	}
1909	if (optind < argc)
1910		open_filesystem(argv[optind], open_flags,
1911				superblock, blocksize, catastrophic,
1912				data_filename);
1913
1914	sci_idx = ss_create_invocation(debug_prog_name, "0.0", (char *) NULL,
1915				       &debug_cmds, &retval);
1916	if (retval) {
1917		ss_perror(sci_idx, retval, "creating invocation");
1918		exit(1);
1919	}
1920	ss_get_readline(sci_idx);
1921
1922	(void) ss_add_request_table (sci_idx, &ss_std_requests, 1, &retval);
1923	if (retval) {
1924		ss_perror(sci_idx, retval, "adding standard requests");
1925		exit (1);
1926	}
1927	if (extra_cmds)
1928		ss_add_request_table (sci_idx, extra_cmds, 1, &retval);
1929	if (retval) {
1930		ss_perror(sci_idx, retval, "adding extra requests");
1931		exit (1);
1932	}
1933	if (request) {
1934		retval = 0;
1935		retval = ss_execute_line(sci_idx, request);
1936		if (retval) {
1937			ss_perror(sci_idx, retval, request);
1938			exit_status++;
1939		}
1940	} else if (cmd_file) {
1941		exit_status = source_file(cmd_file, sci_idx);
1942	} else {
1943		ss_listen(sci_idx);
1944	}
1945
1946	if (current_fs)
1947		close_filesystem();
1948
1949	remove_error_table(&et_ext2_error_table);
1950	return exit_status;
1951}
1952