/*****************************************************************************/
/* The development of this program is partly supported by IPA                */
/* (Information-Technology Promotion Agency, Japan).                         */
/*****************************************************************************/

/*****************************************************************************/
/*  cdavl.c - CUI disk allocation viewer                                     */
/*  Copyright: Copyright (c) Hitachi, Ltd. 2004-2008                         */
/*             Authors: Yumiko Sugita (yumiko.sugita.yf@hitachi.com),        */
/*                      Satoshi Fujiwara (sa-fuji@sdl.hitachi.co.jp)         */
/*                                                                           */
/*  This program is free software; you can redistribute it and/or modify     */
/*  it under the terms of the GNU General Public License as published by     */
/*  the Free Software Foundation; either version 2 of the License, or        */
/*  (at your option) any later version.                                      */
/*                                                                           */
/*  This program is distributed in the hope that it will be useful,          */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
/*  GNU General Public License for more details.                             */
/*                                                                           */
/*  You should have received a copy of the GNU General Public License        */
/*  along with this program; if not, write to the Free Software              */
/*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA      */
/*****************************************************************************/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ext2fs/ext2_fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <mntent.h>
#include "../common/util.h"
#include "../drv/liveinfo_app.h"
#include "cdavl.h"
#include <endian.h>
#if BYTE_ORDER == LITTLE_ENDIAN
#  include <linux/byteorder/little_endian.h>
#else
#  include <linux/byteorder/big_endian.h>
#endif

#define le16_to_cpu	__le16_to_cpu
#define le32_to_cpu	__le32_to_cpu



int is_mount(char* devname) {
	FILE		*f;
	struct mntent	*m = NULL;
	int		is_mnt = 0;

	f = uSetmntent(MOUNTED, "r");
	if (!f)
		return is_mnt;
	while ((m = getmntent(f)) != NULL) {
		if (strcmp(devname, m->mnt_fsname + 5) == 0) {
						/* 5: strlen of "/dev/" */
			is_mnt = 1;
			break;
		}
	}
	endmntent(f);
	return is_mnt;
}

int chkMountState(t_prog_const* prog_c, t_e2info* fs) {
	FILE		*f;
	struct mntent	*m = NULL;

	f = uSetmntent(MOUNTED, "r");
	if (!f)
		return ERROR;
	while ((m = getmntent(f)) != NULL) {
		if (strcmp(prog_c->dev_name, m->mnt_fsname) == 0) {
			strncpy(fs->mnt_dir, m->mnt_dir, FNAME_MAX);
			break;
		}
	}
	endmntent(f);
	return SUCCESS;
}

void printSuperBlock(t_e2info* fs) {
	struct ext2_super_block	*sb = &fs->sb;

	printf("------ super block ------\n");
	PRINT4("s_inodes_count:           ", le32_to_cpu(sb->s_inodes_count));
	PRINT4("s_blocks_count:           ", le32_to_cpu(sb->s_blocks_count));
	PRINT4("s_r_blocks_count:         ", le32_to_cpu(sb->s_r_blocks_count));
	PRINT4("s_free_blocks_count:      ", le32_to_cpu(sb->s_free_blocks_count));
	PRINT4("s_free_inodes_count:      ", le32_to_cpu(sb->s_free_inodes_count));
	PRINT4("s_first_data_block:       ", le32_to_cpu(sb->s_first_data_block));
	PRINT4("s_log_block_size:         ", le32_to_cpu(sb->s_log_block_size));
	PRINT4("s_log_frag_size:          ", le32_to_cpu(sb->s_log_frag_size));
	PRINT4("s_blocks_per_group:       ", le32_to_cpu(sb->s_blocks_per_group));
	PRINT4("s_frags_per_group:        ", le32_to_cpu(sb->s_frags_per_group));
	PRINT4("s_inodes_per_group:       ", le32_to_cpu(sb->s_inodes_per_group));
	PRINT4("s_mtime:                  ", le32_to_cpu(sb->s_mtime));
	PRINT4("s_wtime:                  ", le32_to_cpu(sb->s_wtime));
	PRINT2("s_mnt_count:              ", le16_to_cpu(sb->s_mnt_count));
	PRINT2("s_max_mnt_count:          ", le16_to_cpu(sb->s_max_mnt_count));
	PRINT2("s_magic:                  ", (__u16)le16_to_cpu(sb->s_magic));
	PRINT2("s_state:                  ", le16_to_cpu(sb->s_state));
	PRINT2("s_errors:                 ", le16_to_cpu(sb->s_errors));
	PRINT2("s_minor_rev_level:        ", le16_to_cpu(sb->s_minor_rev_level));
	PRINT4("s_lastcheck:              ", le32_to_cpu(sb->s_lastcheck));
	PRINT4("s_checkinterval:          ", le32_to_cpu(sb->s_checkinterval));
	PRINT4("s_creator_os:             ", le32_to_cpu(sb->s_creator_os));
	PRINT4("s_rev_level:              ", le32_to_cpu(sb->s_rev_level));
	PRINT2("s_def_resuid:             ", le16_to_cpu(sb->s_def_resuid));
	PRINT2("s_def_resgid:             ", le16_to_cpu(sb->s_def_resgid));
	PRINT4("s_first_ino:              ", le32_to_cpu(sb->s_first_ino));
	PRINT2("s_inode_size:             ", le16_to_cpu(sb->s_inode_size));
	PRINT2("s_block_group_nr:         ", le16_to_cpu(sb->s_block_group_nr));
	PRINT4("s_feature_compat:         ", le32_to_cpu(sb->s_feature_compat));
	PRINT4("s_feature_incompat:       ", le32_to_cpu(sb->s_feature_incompat));
	PRINT4("s_feature_ro_compat:      ", le32_to_cpu(sb->s_feature_ro_compat));
	printf("s_uuid:\n");
	uDumpN((char*)sb->s_uuid, 16);
	PRINTS("s_volume_name:            ", sb->s_volume_name, 16);
	PRINTS("s_last_mounted:           ", sb->s_last_mounted, 64);
	PRINT4("s_algorithm_usage_bitmap: ", le32_to_cpu(sb->s_algorithm_usage_bitmap));
	PRINT1("s_prealloc_blocks:        ", sb->s_prealloc_blocks);
	PRINT1("s_prealloc_dir_blocks:    ", sb->s_prealloc_dir_blocks);
	if (le32_to_cpu(sb->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
		printf("s_journal_uuid:\n");
		uDumpN((char*)sb->s_journal_uuid, 16);
		PRINT4("s_journal_inum:           ", le32_to_cpu(sb->s_journal_inum));
		PRINT4("s_journal_dev:            ", le32_to_cpu(sb->s_journal_dev));
		PRINT4("s_last_orphan:            ", le32_to_cpu(sb->s_last_orphan));
	}
#ifdef NEW_KERNEL
	printf("s_hash_seed:\n");
	uDumpN((char*)sb->s_hash_seed, 4);
	PRINT1("s_def_hash_version:       ", sb->s_def_hash_version);
	PRINT4("s_default_mount_opts:     ", le32_to_cpu(sb->s_default_mount_opts));
	PRINT4("s_first_meta_bg:          ", le32_to_cpu(sb->s_first_meta_bg));
#endif
	printf("\n");
}

int getSuperBlock(t_prog_const* prog_c, t_e2info* fs) {
	int	rc;

	rc = uSeekRead(prog_c->fd, SB_OFFSET, &fs->sb,
		       sizeof(struct ext2_super_block));
	if (rc < 0)
		return ERROR;

	/* check s_magic */
	if ((__u16)le16_to_cpu(fs->sb.s_magic) != EXT2_SUPER_MAGIC) {
		fprintf(stderr,
			"It's not ext2/ext3 file system.(s_magic: %08x)\n",
			(__u16)le16_to_cpu(fs->sb.s_magic));
		return ERROR;
	}

	if (prog_c->verbose_level >= 2)
		printSuperBlock(fs);

	return SUCCESS;
}

int getGroupDesc(t_prog_const* prog_c, t_e2info* fs) {
	int		i, rc, size = sizeof(struct ext2_group_desc);
	off_t		pos;

	pos = (off_t)(le32_to_cpu(fs->sb.s_first_data_block) + 1) * fs->bs;
	for (i = 0; i < fs->gc; i++) {
		rc = uSeekRead(prog_c->fd, pos, &fs->gd[i], size);
		if (rc < 0)
			return ERROR;
		pos += size;
	}
	return SUCCESS;
}



int isSysBlockFragment(t_e2info* fs, unsigned int prev_bn, unsigned int bn) {
	t_sys_blk_info		*sysb = NULL;
	unsigned int		key, min, mid, max;

	if (prev_bn > bn)
		/* is not continuous block */
		return FALSE;

	key = prev_bn + 1;
	min = 0;
	max = fs->gc * 2 - 1;
	while (min <= max) {
		mid = (min + max) / 2;
		sysb = &fs->sysb_info[mid];
		if (!SYSB_INFO_IS_SET(sysb)) {
			max = mid - 1;
			continue;
		}
		if (sysb->start == key)
			return (sysb->end == bn - 1);
		else if (sysb->start > key)
			max = mid - 1;
		else
			min = mid + 1;
	}
	return FALSE;
}

int setSysBlockInfo(t_e2info* fs, unsigned int start, unsigned int n) {
	t_sys_blk_info		*sysb = fs->sysb_info;
	unsigned int		i, end = start + n - 1;

	if (!n)
		return SUCCESS;

	for (i = 0; i < fs->gc * 2; i++) {
		if (SYSB_INFO_IS_SET(sysb)) {
			if (start >= sysb->start && end <= sysb->end) {
				fprintf(stderr,
					"group descriptor is corrupted.\n");
				return ERROR;
			}
			if (start == sysb->end + 1) {
				sysb->end += n;
				return SUCCESS;
			}
		} else {
			sysb->start = start;
			sysb->end = end;
			return SUCCESS;
		}
		sysb++;
	}
	return ERROR;
}

void printSysBlockInfo(t_e2info* fs) {
	t_sys_blk_info		*sysb = fs->sysb_info;
	unsigned int		i;

	printf("------ system block info ------\n");
	for (i = 0; i < fs->gc * 2; i++) {
		if (!SYSB_INFO_IS_SET(sysb)) {
			printf("\n");
			return;
		}
		printf("system block: %u<->%u\n", sysb->start, sysb->end);
		sysb++;
	}
	printf("\n");
}

unsigned int getSysBlocks(t_e2info* fs) {
	t_sys_blk_info		*sysb = fs->sysb_info;
	unsigned int		i, cnt = 0;

	for (i = 0; i < fs->gc * 2; i++) {
		if (!SYSB_INFO_IS_SET(sysb))
			return cnt;
		cnt += sysb->end - sysb->start + 1;
		sysb++;
	}
	return cnt;
}

unsigned int getInodeTblBlocks(t_e2info* fs) {
	return le32_to_cpu(fs->sb.s_inodes_per_group) * le16_to_cpu(fs->sb.s_inode_size) / fs->bs;
}

int chkSysBlocks(t_prog_const* prog_c, t_e2info* fs, t_chk* chk) {
	unsigned int		gn, start = 0, end, i, j, max;
	struct ext2_group_desc	*gd;
	t_bchk			*bchk = chk->blocks;

	max = getInodeTblBlocks(fs);
	for (gn = 0; gn < fs->gc; gn++) {
		gd = &fs->gd[gn];
		if (gn != 0)
			start = le32_to_cpu(fs->sb.s_first_data_block) +
				le32_to_cpu(fs->sb.s_blocks_per_group) * gn;
		end = le32_to_cpu(gd->bg_block_bitmap);

		for (i = start; i < end; i++)
			bchk[i].type = BT_FS1;
		if (setSysBlockInfo(fs, start, end - start) == ERROR)
			return ERROR;
		bchk[le32_to_cpu(gd->bg_block_bitmap)].type = BT_FS2;
		if (setSysBlockInfo(fs, le32_to_cpu(gd->bg_block_bitmap), 1) == ERROR)
			return ERROR;
		bchk[le32_to_cpu(gd->bg_inode_bitmap)].type = BT_FS2;
		if (setSysBlockInfo(fs, le32_to_cpu(gd->bg_inode_bitmap), 1) == ERROR)
			return ERROR;

		j = le32_to_cpu(gd->bg_inode_table);
		for (i = 0; i < max; i++)
			bchk[j++].type = BT_UNUSED_INODE;
		if (setSysBlockInfo(fs, le32_to_cpu(gd->bg_inode_table), max) == ERROR)
			return ERROR;
	}
	if (prog_c->verbose_level >= 3)
		printSysBlockInfo(fs);
	return SUCCESS;
}

void printPath(t_chk* chk, unsigned int ino) {
	int	i;

	printf("\"/");
	for (i = 1; i <= chk->depth; i++) {
		printf("%s", chk->name[i]);
		if (i != chk->depth)
			printf("/");
	}
	printf("\"(ino: %u)", ino);
}

char charOfBlockType(unsigned char type) {
	switch (type) {
	case BT_UNUSED_BLOCK:		return '.';
	case BT_FS1:			return 'S';
	case BT_FS2:			return 's';
	case BT_XATTR:			return 'E';
	case BT_UNUSED_INODE:		return 'i';
	case BT_USED_INODE:		return 'I';
	case BT_USED_BLOCK:		return 'o';
	case BT_FRAG_BLOCK:		return 'x';
	case BT_FRAG_BLOCK|BF_FRAG:	return 'X';
	case BT_FRAG_BLOCK|BF_SYS_FRAG:	return 'Y';
	default:
					return '?';
	}
}

char colorOfBlockType(unsigned char type) {
	switch (type) {
	case BT_FS1: case BT_FS2:
	case BT_UNUSED_INODE: case BT_USED_INODE:
					return 0x20;
	case BT_XATTR:			return 0x22;
	case BT_USED_BLOCK:		return 0x24;
	case BT_FRAG_BLOCK:		return 0x23;
	case BT_FRAG_BLOCK|BF_FRAG:	return 0x21;
	case BT_FRAG_BLOCK|BF_SYS_FRAG:	return 0x1f;
	default:
					return 0;
	}
}



void freeBlockSeq(t_blk_seq* p) {
	t_blk_seq	*p2 = p;

	while (p) {
		p2 = p->next;
		free(p);
		p = p2;
	}
}

void printFileBlockSeq(t_blk_seq* p, int is_fragment) {
	unsigned char	bt;
	char		c;

	bt = is_fragment ? BT_FRAG_BLOCK : BT_USED_BLOCK;
	while (p) {
		c = charOfBlockType(bt | p->type);
		printf("%c%u", c, p->cnt);
		if (p->next)
			printf(",");
		p = p->next;
	}
	printf("\n");
}

void printInode(unsigned int ino, struct ext2_inode* p) {
#ifdef	DEBUG
	printf("------ inode: %d ------\n", ino);
	PRINT2("i_mode:        ", le16_to_cpu(p->i_mode));
	PRINT2("i_uid:         ", le16_to_cpu(p->i_uid));
	PRINT4("i_size:        ", le32_to_cpu(p->i_size));
	PRINT4("i_atime:       ", le32_to_cpu(p->i_atime));
	PRINT4("i_ctime:       ", le32_to_cpu(p->i_ctime));
	PRINT4("i_mtime:       ", le32_to_cpu(p->i_mtime));
	PRINT4("i_dtime:       ", le32_to_cpu(p->i_dtime));
	PRINT2("i_gid:         ", le16_to_cpu(p->i_gid));
	PRINT2("i_links_count: ", le16_to_cpu(p->i_links_count));
	PRINT4("i_blocks:      ", le32_to_cpu(p->i_blocks));
	PRINT4("i_block[0]:    ", le32_to_cpu(p->i_block[0]));
	PRINT4("i_block[1]:    ", le32_to_cpu(p->i_block[1]));
	PRINT4("i_block[2]:    ", le32_to_cpu(p->i_block[2]));
	PRINT4("i_block[3]:    ", le32_to_cpu(p->i_block[3]));
	PRINT4("i_block[4]:    ", le32_to_cpu(p->i_block[4]));
	PRINT4("i_block[5]:    ", le32_to_cpu(p->i_block[5]));
	PRINT4("i_block[6]:    ", le32_to_cpu(p->i_block[6]));
	PRINT4("i_block[7]:    ", le32_to_cpu(p->i_block[7]));
	PRINT4("i_block[8]:    ", le32_to_cpu(p->i_block[8]));
	PRINT4("i_block[9]:    ", le32_to_cpu(p->i_block[9]));
	PRINT4("i_block[10]:   ", le32_to_cpu(p->i_block[10]));
	PRINT4("i_block[11]:   ", le32_to_cpu(p->i_block[11]));
	PRINT4("i_block[12]:   ", le32_to_cpu(p->i_block[12]));
	PRINT4("i_block[13]:   ", le32_to_cpu(p->i_block[13]));
	PRINT4("i_block[14]:   ", le32_to_cpu(p->i_block[14]));
	PRINT4("i_flags:       ", le32_to_cpu(p->i_flags));
	PRINT4("i_generation:  ", le32_to_cpu(p->i_generation));
	PRINT4("i_file_acl:    ", le32_to_cpu(p->i_file_acl));
	PRINT4("i_dir_acl:     ", le32_to_cpu(p->i_dir_acl));
	PRINT4("i_faddr:       ", le32_to_cpu(p->i_faddr));
	PRINT1("l_i_frag:      ", p->osd2.linux2.l_i_frag);
	PRINT1("l_i_fsize:     ", p->osd2.linux2.l_i_fsize);
	PRINT2("l_i_uid_high:  ", le16_to_cpu(p->osd2.linux2.l_i_uid_high));
	PRINT2("l_i_gid_high:  ", le16_to_cpu(p->osd2.linux2.l_i_gid_high));
	printf("\n");
#endif
}

int getInode(t_prog_const* prog_c, t_e2info* fs, t_chk* chk,
	     unsigned int ino, t_inode_info* i_info, unsigned char found) {
	unsigned int		gn, offset, sz_inode, ipb, bn, rc;
	off_t			pos;
	struct ext2_group_desc	*gd;
	int			inode_read = FALSE;
	t_inode_cache		*cache = &fs->inode_cache;

	if (prog_c->liveinfo_fd < 0 &&
	    (ino >= le32_to_cpu(fs->sb.s_first_ino) || ino == EXT2_ROOT_INO))
		inode_read = TRUE;
	ino--;
	gn = ino / le32_to_cpu(fs->sb.s_inodes_per_group);
	offset = ino % le32_to_cpu(fs->sb.s_inodes_per_group);

	sz_inode = le16_to_cpu(fs->sb.s_inode_size);
	gd = &fs->gd[gn];
	ipb = fs->bs / sz_inode;
	bn = le32_to_cpu(gd->bg_inode_table) + offset / ipb;

	if (inode_read) {
		unsigned int	cnt, hit = 0;

		if (ino >= cache->top_ino && ino < cache->top_ino + cache->cnt)
			hit = 1;
#ifdef	DEBUG
		printf("inode-cache %s (%u: %u<->%u)\n",
		       (hit ? "hit" : "miss"),
		       ino + 1,
		       cache->top_ino + 1,
		       cache->top_ino + cache->cnt + 1);
#endif
		if (!hit) {
			pos = (off_t)bn * fs->bs + (offset % ipb) * sz_inode;
			cnt = le32_to_cpu(fs->sb.s_inodes_per_group) - offset;
			if (cnt > INODE_CACHE_MAX)
				cnt = INODE_CACHE_MAX;
			rc = uSeekRead(prog_c->fd, pos, cache->p,
				       sz_inode * cnt);
			if (rc < 0)
				return ERROR;
			cache->top_ino = ino;
			cache->cnt = cnt;
		}
		memcpy(&i_info->inode, &cache->p[ino - cache->top_ino],
		       sz_inode);
	}
	if (found) {
		if (inode_read)
			printInode(ino + 1, &i_info->inode);
		chk->blocks[bn].type = BT_USED_INODE;
		chk->inodes[++ino] = USED_INODE;

		i_info->ino = ino;
		i_info->fblocks = 0;
		i_info->bn_min = le32_to_cpu(fs->sb.s_blocks_count);
		i_info->bn_max = 0;
		i_info->sfrags = 0;
		i_info->frags = 0;
		i_info->prev_bn = 0;
		i_info->blk_seq = i_info->blk_seq_top = NULL;
	}

	return SUCCESS;
}

int chkBlockNum(t_prog_const* prog_c, t_e2info* fs, t_chk* chk,
		t_inode_info* i_info, unsigned int bn) {
	t_bchk		*bchk = &chk->blocks[bn];
	t_blk_seq	*seq = i_info->blk_seq;

	if (bchk->in_proc || bchk->type != BT_UNUSED_BLOCK) {
		fprintf(stderr, "Error: block-%u double checked.\n", bn);
		return ERROR;
	}
	if (prog_c->verbose_level >= 3)
		printf("block: %u (ino: %u)\n", bn, i_info->ino);

	/* fragment check */
	if (i_info->fblocks && bn != i_info->prev_bn + 1) {
		if (isSysBlockFragment(fs, i_info->prev_bn, bn)) {
			bchk->type = BF_SYS_FRAG;
			i_info->sfrags++;
		} else {
			bchk->type = BF_FRAG;
			i_info->frags++;
		}
	}
	i_info->prev_bn = bn;

	/* update inode info */
	bchk->in_proc = BF_IN_PROC | chk->depth;
	i_info->fblocks++;
	if (bn < i_info->bn_min)
		i_info->bn_min = bn;
	if (bn > i_info->bn_max)
		i_info->bn_max = bn;

	if (!seq) {
		SET_BLK_SEQ(i_info->blk_seq, bchk->type);
		i_info->blk_seq_top = i_info->blk_seq;
	} else if (seq->type == bchk->type) {
		seq->cnt++;
	} else {
		SET_BLK_SEQ(i_info->blk_seq, bchk->type);
		seq->next = i_info->blk_seq;
	}

	return SUCCESS;
}

int chkXattrBlockNum(t_prog_const* prog_c, t_e2info* fs, t_chk* chk,
		     t_inode_info* i_info) {
	unsigned int	bn = le32_to_cpu(i_info->inode.i_file_acl);
	t_bchk		*bchk = &chk->blocks[bn];

	if (bchk->in_proc && bchk->type != BT_XATTR) {
		fprintf(stderr, "Error: block-%u double checked.\n", bn);
		return ERROR;
	}
	if (prog_c->verbose_level >= 3)
		printf("xattr-block: %u (ino: %u)\n", bn, i_info->ino);
	if (prog_c->mode == CHK_PARTITION)
		bchk->type = BT_XATTR;
	i_info->xattrs++;

	return SUCCESS;
}

int isFpathPartMatch(t_chk* chk, char* p, unsigned char len) {
	return (chk->p_fpath && !strncmp(chk->p_fpath, p, len)
		&& (chk->p_fpath[len] == '/' || chk->p_fpath[len] == '\0'));
}

int isFpathFullMatch(t_chk* chk, char* p, unsigned char len) {
	return (chk->p_fpath[len] == '\0');
}

/* return 0: check next
 *        1: check this
 */
int chkFollow(t_chk* chk, char* p, unsigned char len,
	      unsigned char found, unsigned char *new_found) {
	*new_found = found;
	if (found)
		*new_found += 1;
	else {
		if (!isFpathPartMatch(chk, p, len))
			return 0;
		if (isFpathFullMatch(chk, p, len)) {
			*new_found += 1;
			chk->path_found = TRUE;
		} else
			chk->p_fpath += len + 1;
	}
	return 1;
}

int chkInode(t_prog_const* prog_c, t_e2info* fs, t_chk* chk,
	     unsigned int ino, unsigned char found);

int chkBlock(t_prog_const* prog_c, t_e2info* fs, t_chk* chk,
	     t_inode_info* i_info, unsigned int bn, unsigned char found,
	     unsigned int ind_nest) {
	unsigned char			buf[EXT2_MAX_BLOCK_SIZE];
	int				rc = NEXT;
	unsigned int			i, max, depth;
	unsigned char			new_found;
	unsigned int*			p;
	struct ext2_dir_entry_2*	de;

	if (found) {
		rc = chkBlockNum(prog_c, fs, chk, i_info, bn);
		if (rc == ERROR)
			return ERROR;
	}

	rc = NEXT;
	if (ind_nest) {
		if (READ_BLOCK(prog_c, fs, bn, buf) < 0)
			return ERROR;

		p = (unsigned int*)buf;
		max = fs->bs / sizeof(unsigned int); /*? __u32 ?*/
		ind_nest--;
		for (i = 0; i < max; i++) {
/*			bn = le32_to_cpu(*p++); */
			bn = *p++;
			
			if (bn) {
				rc = chkBlock(prog_c, fs, chk, i_info,
					      bn, found, ind_nest);
				if (rc == ERROR)
					return ERROR;
			}
		}
	} else if (S_ISDIR(le16_to_cpu(i_info->inode.i_mode))) {
		if (found && prog_c->not_follow)
			return NEXT;
		if (READ_BLOCK(prog_c, fs, bn, buf) < 0)
			return ERROR;

		chk->depth++;
		if (chk->depth >= DEPTH_MAX) {
			fprintf(stderr, "path depth over.(>= %u)\n", DEPTH_MAX);
			return ERROR;
		}
		depth = chk->depth;
		if (depth > chk->max_depth)
			chk->max_depth = depth;

		rc = NEXT;
		for (i = 0; i < fs->bs; i += le16_to_cpu(de->rec_len)) {
			de = (struct ext2_dir_entry_2*)(buf + i);
			if (!le16_to_cpu(de->rec_len))
				break;
			strncpy(chk->name[depth], de->name, de->name_len);
			chk->name[depth][de->name_len] = '\0';
			if (!de->name_len ||
			    !strcmp(".", chk->name[depth]) ||
			    !strcmp("..", chk->name[depth]))
				continue;

			if (!chkFollow(chk, de->name, de->name_len,
				       found, &new_found))
				continue;

			rc = chkInode(prog_c, fs, chk, le32_to_cpu(de->inode), new_found);
			if (!found || rc == NOT_FOUND || rc == ERROR)
				break;
		}
		chk->depth--;
	}
	return rc;
}

int isSymLinkWithNoBlocks(t_e2info* fs, struct ext2_inode* inode) {
	unsigned int		i_blocks = inode->i_blocks;

	if (le32_to_cpu(inode->i_file_acl))
		/* only for now. i_file_acl use only one block. */
		i_blocks -= fs->bs / 512;
	return (i_blocks == 0);
}

int chkIBlocksUnuseDevdrv(t_prog_const* prog_c, t_e2info* fs, t_chk* chk,
			  t_inode_info* i_info, unsigned char found) {
	struct ext2_inode	*inode;
	unsigned int		rc, i, bn;
	unsigned char		found_at_child;

	inode = &i_info->inode;
	i_info->is_dir = S_ISDIR(le16_to_cpu(inode->i_mode));
	if (isSymLinkWithNoBlocks(fs, inode)) {
		rc = FOUND;
		goto EXIT;
	}

	found_at_child = found ? FOUND : NOT_FOUND;

	for (i = 0; i < EXT2_NDIR_BLOCKS; i++)
		if ((bn = le32_to_cpu(inode->i_block[i]))) {
			rc = chkBlock(prog_c, fs, chk, i_info, bn, found, 0);
			if (rc == FOUND)
				found_at_child = FOUND;
			else if (rc != NEXT)
				goto EXIT;
		}
	if ((bn = le32_to_cpu(inode->i_block[EXT2_IND_BLOCK]))) {
		rc = chkBlock(prog_c, fs, chk, i_info, bn, found, 1);
		if (rc == FOUND)
			found_at_child = FOUND;
		else if (rc != NEXT)
			goto EXIT;
	}
	if ((bn = le32_to_cpu(inode->i_block[EXT2_DIND_BLOCK]))) {
		rc = chkBlock(prog_c, fs, chk, i_info, bn, found, 2);
		if (rc == FOUND)
			found_at_child = FOUND;
		else if (rc != NEXT)
			goto EXIT;
	}
	if ((bn = le32_to_cpu(inode->i_block[EXT2_TIND_BLOCK]))) {
		rc = chkBlock(prog_c, fs, chk, i_info, bn, found, 3);
		if (rc == FOUND)
			found_at_child = FOUND;
		else if (rc != NEXT)
			goto EXIT;
	}
	return found_at_child;

EXIT:
	return rc;
}

int chkFileBlocks(t_prog_const* prog_c, t_e2info* fs, t_chk* chk,
		  t_inode_info* i_info) {
	int			rc = SUCCESS;
	int			f, len;
	char			*p = NULL, *path;
	unsigned long		i, bn;
	size_t			size = 8192, step = 2048;
	struct stat		*stat = &chk->stat;
	struct fblocks_info	*fbinfo;

	if (!stat->st_blocks ||
	    !(S_ISREG(stat->st_mode) || S_ISDIR(stat->st_mode)))
		return SUCCESS;
	    
	path = chk->depth ? chk->name[chk->depth] : fs->mnt_dir;
	f = open(path, O_RDONLY);
	if (f < 0)
		switch (errno) {
		case ENOENT:
			/* dangling symbolic link */
			return SUCCESS;
		default:
			/*
			fprintf(stderr, "%s open failed. (%s)\n",
				path, strerror(errno));
				*/
			return ERROR;
		}
	p = (char*)uMalloc(size);
	if (p == NULL) {
		rc = ERROR;
		goto RETURN;
	}
	rc = uWrite(prog_c->liveinfo_fd, &f, sizeof(f));
	if (rc < 0) {
		rc = ERROR;
		goto RETURN;
	}
	while ((len = read(prog_c->liveinfo_fd, p, size)) < 0
	       && errno == EXFULL) {
		size += step;
		p = (char*)uRealloc(p, size);
		if (p == NULL) {
			rc = ERROR;
			goto RETURN;
		}
	}
	if (len < 0)
		printf("liveinfo read failed.(%s)\n", strerror(errno));
	else if (len > 0) {
		for (fbinfo = (struct fblocks_info*)p;
		     (char*)fbinfo < p + len; fbinfo++) {
			if (fbinfo == (struct fblocks_info*)p) {
/*				i_info->inode.i_file_acl = cpu_to_le32(fbinfo->bn); */
				i_info->inode.i_file_acl = fbinfo->bn; 
				continue;
			}
			bn = fbinfo->bn;
			for (i = 0; i < fbinfo->cnt; i++) {
				rc = chkBlockNum(prog_c, fs, chk, i_info, bn++);
				if (rc == ERROR)
					goto RETURN;
			}
		}
	}
RETURN:
	if (p)
		free(p);
	close(f);
	return rc;
}

int chkIBlocksUseDevdrv(t_prog_const* prog_c, t_e2info* fs, t_chk* chk,
			t_inode_info* i_info, unsigned char found) {
	unsigned int		rc;
	char			*p;
	DIR			*d;
	struct dirent		*child;
	unsigned char		new_found;

	p = chk->depth ? chk->name[chk->depth] : fs->mnt_dir;
	i_info->is_dir = S_ISDIR(chk->stat.st_mode);
	if (i_info->is_dir) {
		if (chk->depth && i_info->ino == EXT2_ROOT_INO)
			return found ? FOUND : NOT_FOUND;
		if (found) {
			rc = chkFileBlocks(prog_c, fs, chk, i_info);
			if (rc == ERROR)
				return rc;
			if (prog_c->not_follow)
				return FOUND;
		}
		d = opendir(p);
		if (d == NULL)
			return ERROR;
		if (chdir(p) < 0)
			return ERROR;

		chk->depth++;
		if (chk->depth >= DEPTH_MAX) {
			fprintf(stderr, "path depth over.(>= %u)\n", DEPTH_MAX);
			return ERROR;
		}
		if (chk->depth > chk->max_depth)
			chk->max_depth = chk->depth;

		rc = found ? FOUND : NOT_FOUND;
		while ((child = uReaddir(d)) != NULL) {
			if (strcmp(".", child->d_name) != 0 &&
			    strcmp("..", child->d_name) != 0) {
				if (lstat(child->d_name, &chk->stat) < 0) {
					rc = ERROR;
					break;
				}
				strncpy(chk->name[chk->depth], child->d_name,
					FNAME_MAX);

				if (!chkFollow(chk, child->d_name,
					       strlen(child->d_name),
					       found, &new_found))
					continue;

				rc = chkInode(prog_c, fs, chk,
					      chk->stat.st_ino, new_found);
				if (!found || rc == NOT_FOUND || rc == ERROR)
					break;
			}
		}
		chk->depth--;
		if (uChdir("..") < 0)
			return ERROR;
		if (uClosedir(d) < 0)
			return ERROR;
		return rc;
	} else if (chk->stat.st_ino && found) {
		rc = chkFileBlocks(prog_c, fs, chk, i_info);
		if (rc == ERROR)
			return rc;
	}
	return found ? FOUND : NOT_FOUND;
}

int chkInode(t_prog_const* prog_c, t_e2info* fs, t_chk* chk, unsigned int ino,
	     unsigned char found) {
	unsigned int		rc, i, bn;
	t_inode_info		i_info;
	unsigned char		bt, xattr[24];
	t_bchk			*bchk;
	int			is_fragment = FALSE;
	int			is_target_file = FALSE;


#ifdef DEBUG
	printf(">>> %u (", found);
	printPath(chk, ino);
	printf(")\n");
#endif
	i_info.inode.i_file_acl = 0;

	if (ino == 0 || ino > le32_to_cpu(fs->sb.s_inodes_count))
		/* already deleted file */
		return FOUND;

	/* case of mount point, fstat returns root inode */
	if (chk->inodes[ino] == USED_INODE && ino != EXT2_ROOT_INO)
		/* hard link */
		return FOUND;
	rc = getInode(prog_c, fs, chk, ino, &i_info, found);
	if (rc == ERROR)
		goto EXIT;

	if (ino < le32_to_cpu(fs->sb.s_first_ino) && ino != EXT2_ROOT_INO) {
		rc = FOUND;
		goto EXIT;
	}
	if (prog_c->liveinfo_fd < 0)
		rc = chkIBlocksUnuseDevdrv(prog_c, fs, chk, &i_info, found);
	else
		rc = chkIBlocksUseDevdrv(prog_c, fs, chk, &i_info, found);


EXIT:
#ifdef DEBUG
	printf("<<< %u, %u\n", found, rc);
#endif
	if (!found)
		return rc;

	if (rc == NOT_FOUND || rc == ERROR) {
		freeBlockSeq(i_info.blk_seq_top);
		if (rc == ERROR && IS_MOUNT(fs))
			i_info.blk_seq_top = NULL;
		else
			return rc;
	}
	i_info.xattrs = 0;
	if (le32_to_cpu(i_info.inode.i_file_acl) && ino >= le32_to_cpu(fs->sb.s_first_ino))
		chkXattrBlockNum(prog_c, fs, chk, &i_info);
	if (rc == ERROR) {
		/* error on the mounted partition */
		i_info.fblocks = 0;
		i_info.sfrags = 0;
		i_info.frags = 0;
		i_info.xattrs = 0;
	} else {
		/* update check result */
		chk->fblocks += i_info.fblocks;
		if (chk->bn_min > i_info.bn_min)
			chk->bn_min = i_info.bn_min;
		if (chk->bn_max < i_info.bn_max)
			chk->bn_max = i_info.bn_max;
		chk->sfrags += i_info.sfrags;
		chk->frags += i_info.frags;
	}
	/* update chk->blocks */
	is_fragment = (i_info.sfrags || i_info.frags);
	if (is_fragment)
		chk->f_files++;
	else
		chk->c_files++;
	bt = is_fragment ? BT_FRAG_BLOCK : BT_USED_BLOCK;
	bchk = &chk->blocks[i_info.bn_min];
	for (i = i_info.bn_min; i <= i_info.bn_max; i++, bchk++) {
		if (bchk->in_proc == (chk->depth | BF_IN_PROC)) {
			if (rc == ERROR)
				bchk->type = BT_UNUSED_BLOCK;
			else
				bchk->type |= bt;
			bchk->in_proc = 0;
		}
	}

	/* verbose print */
	if (i_info.fblocks)
		bn = i_info.bn_max - i_info.bn_min + 1;
	else
		bn = 0;
	if (prog_c->verbose_level >= 1)
		sprintf((char*)xattr, ", xattr: %u", i_info.xattrs);
	else
		xattr[0] = '\0';
	if (is_fragment) {
		if (prog_c->verbose_level >= 1) {
			int percent;
			percent = i_info.fblocks == 1 ? 0 :
					((unsigned long long)i_info.frags
					 + i_info.sfrags) * 10000
						/ (i_info.fblocks -1);
			printPath(chk, ino);
			printf(" is fragmented (%d.%02d%%," \
			       " total: %u/%u, frag: %u, sfrag: %u)%s\n",
			       percent / 100, percent % 100,
			       i_info.fblocks, bn, i_info.frags, i_info.sfrags,
			       xattr);
			printf("block sequence: ");
			printFileBlockSeq(i_info.blk_seq_top, is_fragment);
		}
	} else {
		if (prog_c->verbose_level >= 2) {
			if (bn) {
				printPath(chk, ino);
				printf(" is continuous (total: %u)%s\n",
				       bn, xattr);
			} else if (rc == ERROR) {
				printPath(chk, ino);
				printf(" couldn't check blocks\n");
			} else {
				printPath(chk, ino);
				printf(" is not using blocks%s\n", xattr);
			}
		}
	}

	/* this file is the target file ? */
	if (found == 1 && prog_c->mode == CHK_FILE &&
	    (prog_c->not_follow || !i_info.is_dir))
		is_target_file = TRUE;

	if (is_target_file && !chk->blk_seq)
		chk->blk_seq = i_info.blk_seq_top;
	else {
		freeBlockSeq(i_info.blk_seq_top);

		/* this case is program bug */
		if (is_target_file) {
			fprintf(stderr, "target file double checked!!!\n");
			return ERROR;
		}
	}

	return FOUND;
}

int chkAllInodes(t_prog_const* prog_c, t_e2info* fs, t_chk* chk) {
	unsigned int	i;
	int		rc;
	unsigned char	found = 0;

	/* checek find path */
	if (chk->p_fpath[0] != '/')
		return SUCCESS;		/* not found */

	if (IS_MOUNT(fs) && !prog_c->not_use_devdrv) {
		prog_c->liveinfo_fd = open(LIVEINFO_PATH, O_RDWR);
		if (prog_c->liveinfo_fd >= 0) {
			rc = lstat(fs->mnt_dir, &chk->stat);
			if (rc < 0)
				return ERROR;
		}
	}
	if (prog_c->liveinfo_fd < 0) {
		fs->inode_cache.p = uCalloc(le16_to_cpu(fs->sb.s_inode_size),
					    INODE_CACHE_MAX);
		if (fs->inode_cache.p == NULL)
			return ERROR;
	}

	if (chk->p_fpath[1] == '\0')
		found++;
	chk->p_fpath++;

	if (prog_c->verbose_level >= 1) {
		if (prog_c->verbose_level < 2 && !prog_c->color
		    && prog_c->tcnts_view && prog_c->tcnts_one_line)
			printf("data for gdavl (cdavl ver: %s)\n", CDAVL_VER);
		printf("------ check start ------\n");
	}

	if (prog_c->mode == CHK_PARTITION)
		for (i = 1; i < le32_to_cpu(fs->sb.s_first_ino); i++) {
			if (i == EXT2_ROOT_INO)
				continue;
			rc = chkInode(prog_c, fs, chk, i, found);
			if (rc == ERROR)
				return rc;
		}

	/* check start from root inode */
	rc = chkInode(prog_c, fs, chk, EXT2_ROOT_INO, found);
	if (rc == ERROR)
		return rc;

	if (prog_c->mode == CHK_PARTITION) {
		chk->bn_min = 0;
		chk->bn_max = le32_to_cpu(fs->sb.s_blocks_count) - 1;
	}

	if (prog_c->verbose_level >= 1)
		printf("\n");

	return rc;
}



int chkBlockBitmap(t_prog_const* prog_c, t_e2info* fs, t_chk* chk) {
	unsigned char		buf[EXT2_MAX_BLOCK_SIZE], mask = 0;
	unsigned int		rc, gn, bn, i, bpg;
	struct ext2_super_block	*sb;
	struct ext2_group_desc	*gd;
	t_bchk			*bchk;
	int			err = FALSE;

	sb = &fs->sb;
	bpg = le32_to_cpu(sb->s_blocks_per_group);
	bn = le32_to_cpu(sb->s_first_data_block);
	bchk = &chk->blocks[bn];
	for (gn = 0; gn < fs->gc; gn++) {
		gd = &fs->gd[gn];

		/* read block bitmap */
		rc = READ_BLOCK(prog_c, fs, le32_to_cpu(gd->bg_block_bitmap), buf);
		if (rc < 0)
			return ERROR;

		/* block compare check */
		for (i = 0; i < bpg && bn < le32_to_cpu(sb->s_blocks_count); i++) {
			if (i % 8 == 0)
				mask = 1;
			else
				mask <<= 1;
			if (bchk->type == BT_UNUSED_BLOCK) {
				/*
				if (prog_c->mode == CHK_FILE)
					continue;
				if ((buf[i / 8] & mask)) {
					printf("block bitmap check error."\
					       "(block: %u is type: %d)\n",
					       bn, bchk->type);
					err = TRUE;
				}
				*/
				continue;
			} else {
				if (!(buf[i / 8] & mask)) {
					printf("block bitmap check error."\
					       "(block: %u is type: %d)\n",
					       bn, bchk->type);
					err = TRUE;
				}
			}
			bn++;
			bchk++;
		}
	}
	return (err ? ERROR : SUCCESS);
}

int chkInodeBitmap(t_prog_const* prog_c, t_e2info* fs, t_chk* chk) {
	unsigned char		buf[EXT2_MAX_BLOCK_SIZE], mask = 0, *ichk;
	unsigned int		rc, gn, i, ipg, ino;
	struct ext2_super_block	*sb;
	struct ext2_group_desc	*gd;
	int			err = FALSE;

	sb = &fs->sb;
	ipg = le32_to_cpu(sb->s_inodes_per_group);
	ichk = chk->inodes;
	ino = 1;
	for (gn = 0; gn < fs->gc; gn++) {
		gd = &fs->gd[gn];

		/* read inode bitmap */
		rc = READ_BLOCK(prog_c, fs, le32_to_cpu(gd->bg_inode_bitmap), buf);
		if (rc < 0)
			return ERROR;

		/* inode compare check */
		for (i = 0; i < ipg && ino <= le32_to_cpu(sb->s_inodes_count); i++) {
			if (i % 8 == 0)
				mask = 1;
			else
				mask <<= 1;
			if (ichk[ino] == UNUSED_INODE) {
				if (prog_c->mode == CHK_FILE)
					continue;
				if ((buf[i / 8] & mask)) {
					printf("inode bitmap check error."\
					       "(inode: %u)\n", ino);
					err = TRUE;
				}
			} else {
				if (!(buf[i / 8] & mask)) {
					printf("inode bitmap check error."\
					       "(inode: %u)\n", ino);
					err = TRUE;
				}
			}
			ino++;
		}
	}
	return (err ? ERROR : SUCCESS);
}

int chkBitmap(t_prog_const* prog_c, t_e2info* fs, t_chk* chk) {
	int		rc1, rc2;

	if (!prog_c->compare_bitmap || !chk->fblocks)
		return SUCCESS;

	printf("------ compare to bitmap ------\n");

	rc1 = chkBlockBitmap(prog_c, fs, chk);
	rc2 = chkInodeBitmap(prog_c, fs, chk);
	if (rc1 == ERROR || rc2 == ERROR)
		return ERROR;

	printf(" ---> success\n\n");

	return SUCCESS;
}



void printColorBlocks(unsigned int start, unsigned int cnt, t_bchk* bchk) {
	unsigned int	i;
	int		new_color;
	static int	color = 0;
	char		c;

	for (i = 0; i < cnt; i++) {
		if (i % 64 == 0)
			printf("\e[0m%10u\t", start + i);

		c = charOfBlockType(bchk->type);
		new_color = colorOfBlockType(bchk->type);
		if (color != new_color) {
			color = new_color;
			printf("\e[%dm%c", color, c);
		} else
			printf("%c", c);

		if (i % 64 == 63) {
			printf("\e[0m\n");
			color = 0;
		}
		bchk++;
	}
	if (cnt % 64)
		printf("\e[0m\n");
}

void printBlocks(unsigned int start, unsigned int cnt, t_bchk* bchk) {
	unsigned int	i;
	char		c;

	for (i = 0; i < cnt; i++) {
		if (i % 64 == 0)
			printf("%10u\t", start + i);

		c = charOfBlockType(bchk->type);
		printf("%c", c);

		if (i % 64 == 63)
			printf("\n");
		bchk++;
	}
	if (cnt % 64)
		printf("\n");
}

void printColorBlockCnts(unsigned int start, unsigned int cnt, t_bchk* bchk,
			 int is_one_line) {
	unsigned int	i, i_save, same_cnt, len, rc;
	char		c, prev_c = '\0';
	char		tmp[2 + 10 + 1];	/* ,%c%10u */
	int		color, prev_color = 0;

	len = 0;
	same_cnt = 0;
	i_save = 0;
	printf("\e[0m%10u\t", start);

	for (i = 0; i < cnt + 1; i++) {
		c = i == cnt ? '\0' : charOfBlockType(bchk->type);
		color = colorOfBlockType(bchk->type);

		if (c != prev_c && same_cnt) {
			rc = sprintf(tmp, "\e[%dm%c%u",
				     prev_color, prev_c, same_cnt);

			if (is_one_line) {
				if (i_save)
					printf("\e[0m,");
				printf("%s", tmp);
			} else {
				/* escape sequence chars */
				rc = prev_color == 0 ? rc - 4 : rc - 5;

				if (len + rc > COLUMNS_SIZE - 18) {
					/* 18: %10u\t, 1: last , */
					printf("\e[0m,\n%10u\t%s",
					       start + i_save, tmp);
					len = rc;
				} else {
					if (i_save) {
						printf("\e[0m,");
						len++;
					}
					printf("%s", tmp);
					len += rc;
				}
			}
			i_save = i;
			same_cnt = 0;
		}
		same_cnt++;
		prev_c = c;
		prev_color = color;
		bchk++;
	}
	printf("\e[0m\n");
}

void printBlockCnts(unsigned int start, unsigned int cnt, t_bchk* bchk,
		    int is_one_line) {
	unsigned int	i, i_save, same_cnt, len, rc;
	char		c, prev_c = '\0';
	char		tmp[2 + 10 + 1];	/* ,%c%10u */

	len = 0;
	same_cnt = 0;
	i_save = 0;
	printf("%10u\t", start);

	for (i = 0; i < cnt + 1; i++) {
		c = i == cnt ? '\0' : charOfBlockType(bchk->type);

		if (c != prev_c && same_cnt) {
			rc = sprintf(tmp, "%c%u", prev_c, same_cnt);

			if (is_one_line) {
				if (i_save)
					printf(",");
				printf("%s", tmp);
			} else {
				if (len + rc > COLUMNS_SIZE - 18) {
					/* 18: %10u\t, 1: last , */
					printf(",\n%10u\t%s", start + i_save,
					       tmp);
					len = rc;
				} else {
					if (i_save) {
						printf(",");
						len++;
					}
					printf("%s", tmp);
					len += rc;
				}
			}
			i_save = i;
			same_cnt = 0;
		}
		same_cnt++;
		prev_c = c;
		bchk++;
	}
	printf("\n");
}

unsigned int countXattrs(unsigned int start, unsigned int cnt, t_bchk* bchk) {
	unsigned int	i, n = 0;

	for (i = 0; i < cnt; i++) {
		if (bchk->type == BT_XATTR)
			n++;
		bchk++;
	}
	return n;
}

int outputResult(t_prog_const* prog_c, t_e2info* fs, t_chk* chk) {
	unsigned int	blocks;
	int		percent;
	t_bchk		*bchk;

	if (!chk->fblocks) {
		if (chk->path_found)
			fprintf(stderr, "\"%s\" is not using blocks.\n",
				prog_c->fpath);
		else
			fprintf(stderr, "\"%s\" not found.\n", prog_c->fpath);
		return ERROR;
	}

	blocks = chk->bn_max - chk->bn_min + 1;
	bchk = &chk->blocks[chk->bn_min];

	printf("------ cdavl result start ------\n");
	printf("fstype\t%s\n",
	       (le32_to_cpu(fs->sb.s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) ?
	       "ext3" : "ext2");
	printf("mount\t%s", IS_MOUNT(fs) ? "mount" : "unmount");
	if (prog_c->liveinfo_fd >= 0)
		printf("(use davl_liveinfo)");
	printf("\n");
	percent = chk->fblocks == 1 ? 0 :
			((unsigned long long)chk->frags + chk->sfrags) * 10000
							/ (chk->fblocks - 1);
	printf("f-per\t%d.%02d%%\n", percent / 100, percent % 100);
	printf("blocks\t%u\n", blocks);
	if (prog_c->mode == CHK_PARTITION) {
		printf("sblocks\t%u\n", getSysBlocks(fs));
		printf("xattrs\t%u\n", countXattrs(chk->bn_min, blocks, bchk));
	} else {
		printf("sblocks\t-\n");
		printf("xattrs\t-\n");
	}
	printf("fblocks\t%u\n", chk->fblocks);
	printf("frags\t%u\n", chk->frags);
	printf("sfrags\t%u\n", chk->sfrags);
	printf("cfiles\t%u\n", chk->c_files);
	printf("ffiles\t%u\n", chk->f_files);
	printf("depth\t%u\n", chk->max_depth);

	if (prog_c->tcnts_view) {
		if (prog_c->color)
			printColorBlockCnts(chk->bn_min, blocks, bchk,
					    prog_c->tcnts_one_line);
		else
			printBlockCnts(chk->bn_min, blocks, bchk,
				       prog_c->tcnts_one_line);
	} else {
		if (prog_c->color)
			printColorBlocks(chk->bn_min, blocks, bchk);
		else
			printBlocks(chk->bn_min, blocks, bchk);
	}

	if (chk->blk_seq) {
		printf("fbseq\t");
		printFileBlockSeq(chk->blk_seq, chk->frags | chk->sfrags);
		freeBlockSeq(chk->blk_seq);
		chk->blk_seq = NULL;
	}

	return SUCCESS;
}



int printPartitionInfo() {
	FILE		*f;
	int		ln, size = 128, rc, result = SUCCESS;
	char		*p, buf[size];
	t_partition	part;

	f = fopen("/proc/partitions", "r");
	if (!f) {
		fprintf(stderr, "fopen failed.(%s)\n", strerror(errno));
		return ERROR;
	}
	printf("------ partitions information ------\n");
	printf("name\t\tmount\t#blocks     \n\n");
	for (ln = 0; ; ln++) {
		p = fgets(buf, size, f);
		if (!p) {
			printf("\n  execute \"cdavl -h\" for help\n");
			break;
		}
		if (ln < 2)	/* 2: number of information header lines */
			continue;
		rc = sscanf(buf, "%d %d %d %s\n",
			    &part.major, &part.minor, &part.blocks, part.name);
		if (rc < 4) {	/* 4: number of scan datas */
			fprintf(stderr, "fscanf failed.(%d)\n", rc);
			result = ERROR;
			break;
		}
		printf("/dev/%s\t%s\t%10d\n",
		       part.name, is_mount(part.name) ? "  mount" : "unmount",
		       part.blocks);
	}
	fclose(f);
	return result;
}

void usage(void) {
	fprintf(stderr, "cdavl %s\n", CDAVL_VER);
	fprintf(stderr, "    %s\n\n", COPYRIGHT);
	fprintf(stderr, "cdavl [-cChnstTv[v[v]]] [devname [path]]\n");
	fprintf(stderr, "  -c: color display\n");
	fprintf(stderr, "  -C: check result compare to block/inode bitmap" \
		              " (for debug)\n");
	fprintf(stderr, "  -h: show this help message\n");
	fprintf(stderr, "  -n: do not follow directory tree\n");
	fprintf(stderr, "  -s: do not use device-driver interface\n");
	fprintf(stderr, "      (this option takes effect" \
			      " only if the partition is mounted)\n");
	fprintf(stderr, "  -t: type-counts display\n");
	fprintf(stderr, "  -T: type-counts display (one line)\n");
	fprintf(stderr, "  -v(v(v)): verbose mode\n");
}

int parseArgument(int argc, char* argv[], t_prog_const* prog_c) {
	int		i, j, len;

	for (i = 1; i < argc; i++) {
		if (argv[i][0] == '-') {
			len = strlen(argv[i]);
			for (j = 1; j < len; j++) {
				switch (argv[i][j]) {
				case 'c':
					prog_c->color = 1;
					break;
				case 'C':
					prog_c->compare_bitmap = 1;
					break;
				case 'n':
					prog_c->not_follow = 1;
					break;
				case 't':
					prog_c->tcnts_view = 1;
					break;
				case 's':
					prog_c->not_use_devdrv = 1;
					break;
				case 'T':
					prog_c->tcnts_view = 1;
					prog_c->tcnts_one_line = 1;
					break;
				case 'v':
					prog_c->verbose_level++;
					if (prog_c->verbose_level > 3) {
						usage();
						return ERROR;
					}
					break;
				default:
					usage();
					return ERROR;
				}
			}
		}
		else if (!prog_c->dev_name)
			prog_c->dev_name = argv[i];
		else if (!prog_c->fpath)
			prog_c->fpath = argv[i];
		else {
			usage();
			return ERROR;
		}
	}
	if (!prog_c->dev_name) {
		printPartitionInfo();
		return SUCCESS;
	}
	if (!prog_c->fpath) {
		prog_c->mode = CHK_PARTITION;
		prog_c->fpath = "/";
		prog_c->not_follow = 0;	/* ignore "not follow" option */
	} else
		prog_c->mode = CHK_FILE;
	return SUCCESS;
}

int main(int argc, char* argv[]) {
	int			rc, ecode = 0;
	t_prog_const		prog_c;
	t_e2info		fs;
	t_chk			chk;

	/* initialize local variable */
	bzero(&prog_c, sizeof(t_prog_const));
	bzero(&fs, sizeof(t_e2info));
	bzero(&chk, sizeof(t_chk));

	rc = parseArgument(argc, argv, &prog_c);
	if (rc == ERROR)
		goto ERR_EXIT;
	if (!prog_c.dev_name)
		goto EXIT;

	rc = chkMountState(&prog_c, &fs);
	if (rc == ERROR)
		goto ERR_EXIT;

	prog_c.fd = uOpen(prog_c.dev_name, O_RDONLY);
	if (prog_c.fd < 0)
		goto ERR_EXIT;
	prog_c.liveinfo_fd = -1;

	/* read super block */
	rc = getSuperBlock(&prog_c, &fs);
	if (rc == ERROR)
		goto ERR_EXIT;

	fs.bs = EXT2_MIN_BLOCK_SIZE << le32_to_cpu(fs.sb.s_log_block_size);
	fs.gc = (le32_to_cpu(fs.sb.s_blocks_count) - le32_to_cpu(fs.sb.s_first_data_block) +
		   le32_to_cpu(fs.sb.s_blocks_per_group) - 1) / le32_to_cpu(fs.sb.s_blocks_per_group);
	fs.sysb_info = uCalloc(sizeof(t_sys_blk_info), fs.gc * 2); 
					/* '2' is for sparse super */
	if (fs.sysb_info == NULL)
		goto ERR_EXIT;

	/* read group descriptor */
	fs.gd = uCalloc(sizeof(struct ext2_group_desc), fs.gc);
	if (fs.gd == NULL)
		goto ERR_EXIT;

	rc = getGroupDesc(&prog_c, &fs);
	if (rc == ERROR)
		goto ERR_EXIT;

	/* initialize chk variable */
	chk.blocks = uMalloc(sizeof(t_bchk) * le32_to_cpu(fs.sb.s_blocks_count));
	if (chk.blocks == NULL)
		goto ERR_EXIT;
	memset(chk.blocks, BT_UNUSED_BLOCK, le32_to_cpu(fs.sb.s_blocks_count));
	chk.inodes = uMalloc(sizeof(unsigned char) * le32_to_cpu(fs.sb.s_inodes_count));
	if (chk.inodes == NULL)
		goto ERR_EXIT;
	memset(chk.inodes, UNUSED_INODE, le32_to_cpu(fs.sb.s_inodes_count));
	chk.p_fpath = prog_c.fpath;
	chk.depth = 0;
	chk.path_found = FALSE;
	chk.bn_min = le32_to_cpu(fs.sb.s_blocks_count);
	chk.bn_max = 0;

	/* check system block */
	rc = chkSysBlocks(&prog_c, &fs, &chk);
	if (rc == ERROR)
		goto ERR_EXIT;

	/* check start from root inode */
	rc = chkAllInodes(&prog_c, &fs, &chk);
	if (rc == ERROR)
		goto ERR_EXIT;

	/* check bitmap */
	chkBitmap(&prog_c, &fs, &chk);

	/* output check result */
	rc = outputResult(&prog_c, &fs, &chk);
	if (rc == ERROR)
		goto ERR_EXIT;

	goto EXIT;

ERR_EXIT:
	ecode = 1;
EXIT:
	if (fs.gd)
		free(fs.gd);
	if (fs.sysb_info)
		free(fs.sysb_info);
	if (fs.inode_cache.p)
		free(fs.inode_cache.p);
	if (chk.blocks)
		free(chk.blocks);
	if (chk.inodes)
		free(chk.inodes);
	fflush(stderr);
	exit(ecode);
}
