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