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