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