/*
 * ext2_fs.c
 *
 * Copyright 2003, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * EXT2ファイルシステムドライバー。
 */


#include<types.h>
#include<sys/param.h>
#include<lib.h>
#include<mm.h>
#include<fs.h>
#include<device.h>
#include<proc.h>
#include<time.h>
#include<errno.h>
#include<lock.h>
#include<test.h>
#include<ext2_fs.h>
#include<debug.h>


//#define DEBUG_EXT2_FS 1

#ifdef DEBUG_EXT2_FS
	#define STATIC
	#define INLINE
#else
	#define STATIC static
	#define INLINE	inline
#endif


/************************************************************************************
 *
 * 共通
 *
 *************************************************************************************/


enum{
	INT_LOG = 2,					/* intサイズのバイト数の２を根とする対数。 */
	UINT_LOG = INT_LOG,

	EXT2_BLOCK_BASE=1024,			/* ブロックサイズを計算するためのベースバイト数。 */
	EXT2_BLOCK_BASE_LOG=10,			/* ベースブロックサイズの２を根とする対数。 */

	EXT2_SUPER_BLK_POSITION=1024,	/* スーパーブロックの位置（先頭からのバイト数）。 */

	EXT2_GROUP_DESC_BLK=1,			/* group descriptor block number. */

	/* File system states at "s_state". */
	EXT2_VALID_FS=0x0001,			/* Unmounted cleanly */
	EXT2_ERROR_FS=0x0002,			/* Errors detected */

	/* Maximal mount counts. */
	EXT2_DFL_MAX_MNT_COUNT=20,
	EXT2_DFL_CHECKINTERVAL=0,

	/* Magic number at "s_magic". */
	EXT2_SUPER_MAGIC=0xEF53,

	/* Behaviour when detecting errors. */
	EXT2_ERRORS_CONTINUE=1,			/* continue as if nothing happened. */
	EXT2_ERRORS_RO=2,				/* remount read-only. */
	EXT2_ERRORS_PANIC=3,			/* cause a kernel panic. */
	EXT2_ERRORS_DEFAULT=EXT2_ERRORS_CONTINUE,

	/* revision level value. */
	EXT2_GOOD_OLD_REV=0,			/* original format. */
	EXT2_DYNAMIC_REV=1,				/* V2 format with dynamic inode sizes. */

	/* EXT2_*_INO values at "s_first_ino". */
	EXT2_BAD_INO=0x01, 				/* bad blocks inode */
	EXT2_ROOT_INO=0x02, 			/* root directory inode */
	EXT2_ACL_IDX_INO=0x03, 			/* ACL index inode (deprecated?) */
	EXT2_ACL_DATA_INO=0x04, 		/* ACL data inode (deprecated?) */
	EXT2_BOOT_LOADER_INO=0x05,		/* boot loader inode */
	EXT2_UNDEL_DIR_INO=0x06,		/* undelete directory inode */
};

/* super block. */
typedef struct{
	uint s_inodes_count;			/* 00,total number of inodes. */
	uint s_blocks_count;			/* 04,total number of blocks. */
	uint s_r_blocks_count;			/* 08,total number of blocks reserved for the usage of the super user. */
	uint s_free_blocks_count;		/* 0c,total number of free blocks. */
	uint s_free_inodes_count;		/* 10,total number of free inodes. */
	uint s_first_data_block;		/* 14,the block containing the superblock structure.block size larger than 1KB=0,block size of 1KB=1. */
	uint s_log_block_size;			/* 18,number of bits to shift left the value 1024 for computing block size. */
	int s_log_frag_size;			/* 1c,number of bits to shift the value 1024 for computing fragment size. */
	uint s_blocks_per_group;		/* 20,total number of blocks per group. */
	uint s_frags_per_group;			/* 24,total number of fragments per group. */
	uint s_inodes_per_group;		/* 28,total number of inodes per group. */
	uint s_mtime;					/* 2c,Unix time,last time the file system was mounted. */
	uint s_wtime;					/* 30,Unix time,last write access to the file system. */
	ushort s_mnt_count;				/* 34,how many time the file system was mounted. */
	short s_max_mnt_count;			/* 36,the maximum number of mount times. */
	ushort s_magic;					/* 38,magic number 0xEF53. */
	ushort s_state;					/* 3a,file system state. */
	ushort s_errors;				/* 3c,behaviour when an error is detected. */
	ushort s_minor_rev_level;		/* 3e,minor revision level. */
	uint s_lastcheck;				/* 40,Unix time,last file system check. */
	uint s_checkinterval;			/* 44,between file system checks. */
	uint s_creator_os;				/* 48,os that created the file system. */
	uint s_rev_level;				/* 4c,revision level value. */
	ushort s_def_resuid;			/* 4e,default user id for reserved blocks. */
	ushort s_def_resgid;			/* 50,default group id for reserved blocks. */

	/* EXT2_DYNAMIC_REV Specific */
	uint s_first_ino;				/* 54,index to the first inode useable for standard files. */
	ushort s_inode_size;			/* 58,size of the inode structure. */
	ushort s_block_group_nr;		/* 5a,block group number hosting this superblock. */
	uint s_feature_compat;			/* 5c,bitmask of compatible features. */
	uint s_feature_incompat;		/* 60,bitmask of incompatible features. */
	uint s_feature_ro_compat;		/* 64,bitmask of “read-only” features. */
	uchar s_uuid[16];				/* 68,volume id. */
	char s_volume_name[16];			/* 78,volume name,mostly unusued. */
	char s_last_mounted[64];		/* 88,last mounted directory path,not normally used. */
	uint s_algo_bitmap;				/* compression algorithms. */

	/* Performance Hints */
	uchar s_prealloc_blocks;		/* nmber of blocks to try to preallocate. */
	uchar s_prealloc_dir_blocks;	/* number to preallocate for dirs. */
	ushort s_padding1;

	/* Journaling Support */
	uchar s_journal_uuid[16];		/*  */
	uint s_journal_inum;			/*  */
	uint s_journal_dev;				/*  */
	uint s_last_orphan;				/*  */

	/* メモリー保存用独自定義。 */
	int blockSize;					/* ブロックサイズ。 */
	int sectorSize;					/* セクターサイズ。 */
	int sectors;					/* 1ブロックのセクター数。 */
}SUPER_BLOCK;

/* groupe descriptor. */
typedef struct{
	uint bg_block_bitmap;			/* 0,the first block of the "block bitmap". */
	uint bg_inode_bitmap;			/* 4,the first block of the "inode bitmap". */
	uint bg_inode_table;			/* 8,the first block of the "inode table". */
	ushort bg_free_blocks_count;	/* c,total number of free blocks. */
	ushort bg_free_inodes_count;	/* e,total number of free inodes. */
	ushort bg_used_dirs_count;		/* 10,number of inodes allocated to directories. */
	ushort bg_pad;
	uint bg_reserved[3];
}GROUP_DESC;


/*================================== パブリック ========================================*/


/* ビットマップでバイト値から空きブロックの位置を求める。 */
static const uchar emptyBit[]={
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
	0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,0,
};


// マウント時に代入
static SUPER_BLOCK *superBlock[MAX_DEVICE_OPEN];
static GROUP_DESC *grpDesc[MAX_DEVICE_OPEN];
static int grpDescNum[MAX_DEVICE_OPEN];			// グループディスクリプタ数
static int grpDescBlocks[MAX_DEVICE_OPEN];		// グループディスクリプタブロック数


/*
 * ブロック数からセクター数を算出する。
 * parameters : device inode,block
 * return : secter number
 */
STATIC INLINE uint blkToSect(int din,uint blk)
{
	return superBlock[din]->blockSize * blk / superBlock[din]->sectorSize;
}


/*
 * スーパーブロックとグループディスクリプタをディスクに書き戻す。
 * return : 0 or error=-1
 */
STATIC int writeBackSbGd(int din)
{
	SUPER_BLOCK *sb = superBlock[din];
	int sectors = sb->sectors;
	void *buf;


	/* グループディスクリプタをディスクに書き戻す。 */
	write_cache(din, grpDesc[din], grpDescBlocks[din] * sectors, blkToSect(din, EXT2_GROUP_DESC_BLK + sb->s_first_data_block));
	write_cache(din, grpDesc[din], grpDescBlocks[din] * sectors,
		blkToSect(din, EXT2_GROUP_DESC_BLK + sb->s_first_data_block + sb->s_blocks_per_group));

	/* スーパーブロックをディスクに書き戻す。 */
	buf = kmalloc(sb->blockSize);
	if (buf==NULL){
		return -1;
	}
	/*read_cache(din,buf,sectors,blkToSect(din,sb->s_first_data_block));*/
	memset(buf, 0, sb->blockSize);
	if (sb->s_first_data_block == 1){
		memcpy(buf, sb, sizeof(SUPER_BLOCK));
	}
	else{
		memcpy((char*)buf + EXT2_SUPER_BLK_POSITION, sb, sizeof(SUPER_BLOCK));
	}
	write_cache(din, buf, sectors, blkToSect(din, sb->s_first_data_block));
	if (sb->s_first_data_block == 0){
		memset((char*)buf + EXT2_SUPER_BLK_POSITION, 0,sizeof(SUPER_BLOCK));
		memcpy(buf, sb, sizeof(SUPER_BLOCK));
	}
	write_cache(din, buf, sectors, blkToSect(din, sb->s_first_data_block + sb->s_blocks_per_group));
	kfree(buf);

	return 0;
}


/***********************************************************************
 *
 * Block
 *
 ***********************************************************************/


/* ビットマップ構造体。 */
typedef struct{
	uchar *bitmap;			/* EXT2グループブロックビットマップバッファ。 */
	ushort currentGd;		/* 現在のグループ */
	ushort currentByte;		/* 現在のビットマップのバイトオフセット。 */
}BITMAP;


/*================================== プライベート ========================================*/


static BITMAP blockBm[MAX_DEVICE_OPEN];
static int blkLock[MAX_DEVICE_OPEN];			// ブロック処理スピンロックゲート


/*================================== パブリック ========================================*/


/*
 * 空きブロックを得る。
 * parameters : device inode
 * return : empty block or error number;
 */
STATIC int getBlock(int din)
{
	GROUP_DESC *grpDsc = grpDesc[din];
	SUPER_BLOCK *superBlk = superBlock[din];
	int block;
	int grp, byte;
	int last;
	int rest;

	enter_spinlock(&blkLock[din]);
	{
		// 現在のグループに空きブロックがない
		if (grpDsc[blockBm[din].currentGd].bg_free_blocks_count == 0){
			int sectors = superBlk->sectors;

			// 現在のビットマップを書き戻す
			rest = write_cache(din, blockBm[din].bitmap, sectors, blkToSect(din, grpDsc[blockBm[din].currentGd].bg_block_bitmap));
			if (rest < 0){
				return rest;
			}

			// 空きブロックのあるグループを探す。
			for (grp = blockBm[din].currentGd + 1;; ++grp){
				if (grp == grpDescNum[din]){
					grp = 0;
				}
				if (grp == blockBm[din].currentGd){
					return -ENOSPC;
				}
				if (0 < grpDsc[grp].bg_free_blocks_count){
					break;
				}
			}
			rest = read_cache(din, blockBm[din].bitmap, sectors, blkToSect(din, grpDsc[grp].bg_block_bitmap));
			if (rest < 0){
				return rest;
			}
			blockBm[din].currentGd = grp;
			blockBm[din].currentByte = 0;
		}

		/* 空きビットマップの検索。 */
		last = ROUNDUP_DIV(superBlk->s_blocks_per_group , BYTE_BIT);
		for (byte = blockBm[din].currentByte; blockBm[din].bitmap[byte] == 0xff;){
			if (last == ++byte){
				byte = 0;
			}
			// 空きビットマップがあるはず
			ASSERT(blockBm[din].currentByte != byte)
		}
		blockBm[din].currentByte = byte;
		block = byte * BYTE_BIT + emptyBit[blockBm[din].bitmap[byte]];
		blockBm[din].bitmap[byte] |= 1 << emptyBit[blockBm[din].bitmap[byte]];
		
		/* 空きブロックカウントを減らす。 */
		--grpDsc[blockBm[din].currentGd].bg_free_blocks_count;
		--superBlk->s_free_blocks_count;
	}
	exit_spinlock(&blkLock[din]);

	return block + blockBm[din].currentGd * superBlk->s_blocks_per_group + superBlk->s_first_data_block;
}


/*
 * ブロックを開放する。
 * parameters : device inode,block number
 */
STATIC void releaseBlock(int din,uint block)
{
	SUPER_BLOCK *sb = superBlock[din];
	int sectors = sb->sectors;
	int grp;
	int a;

	/* ブロックビットマップの検索。 */
	grp = (block - sb->s_first_data_block) / sb->s_blocks_per_group;
	enter_spinlock(&blkLock[din]);
	{
		if (grp != blockBm[din].currentGd){
			if (write_cache(din, blockBm[din].bitmap, sectors,
				blkToSect(din, grpDesc[din][blockBm[din].currentGd].bg_block_bitmap)) != sectors){
				return;
			}
			if (read_cache(din, blockBm[din].bitmap, sectors, blkToSect(din, grpDesc[din][grp].bg_block_bitmap)) != sectors){
				return;
			}
			blockBm[din].currentGd = grp;
		}

		/* ビットマップに書き込む。 */
		a = block - grp * sb->s_blocks_per_group - sb->s_first_data_block;
		blockBm[din].bitmap[a / BYTE_BIT] &= ~(1 << a % BYTE_BIT);

		/* ブロックカウントを増やす。 */
		++grpDesc[din][grp].bg_free_blocks_count;
		++sb->s_free_blocks_count;
	}
	exit_spinlock(&blkLock[din]);
}


/*
 * マウント時の初期化。
 * parameters : device inode
 * return : groupe descriptor number or error number
 */
STATIC int initBlock(int din)
{
	SUPER_BLOCK *sb = superBlock[din];
	GROUP_DESC *gd = grpDesc[din];
	int grp, rest;

	blockBm[din].bitmap = kmalloc(sb->blockSize);
	if (blockBm[din].bitmap == NULL){
		return -ENOMEM;
	}
	blockBm[din].currentGd = 0;
	blockBm[din].currentByte = 0;

	// ブロックに空きのあるグループのビットマップを読み出す
	for (grp = 0; gd[grp].bg_free_blocks_count == 0; ++grp){
		if(grp == grpDescNum[din]){
			return -ENOSPC;
		}
	}
	rest = read_direct(din, blockBm[din].bitmap, sb->sectors, blkToSect(din, gd[grp].bg_block_bitmap));
	if (rest != sb->sectors){
		return rest;
	}
	blockBm[din].currentGd = grp;
	blockBm[din].currentByte = 0;

	return blockBm[din].currentGd;
}


/*
 * アンマウント時の処理。
 * parameters : device inode
 */
STATIC void umountBlock(int din)
{
	int sectors = superBlock[din]->sectors;

	write_cache(din, blockBm[din].bitmap, sectors, blkToSect(din, grpDesc[din][blockBm[din].currentGd].bg_block_bitmap));
	kfree(blockBm[din].bitmap);
}


/***********************************************************************
 *
 * Inode
 *
 ***********************************************************************/


enum{
	/* at i_mode */
	EXT2_S_IFMT=  0xF000,	/* format mask */
	EXT2_S_IFSOCK=0xC000,	/* socket */
	EXT2_S_IFLNK= 0xA000,	/* symbolic link */
	EXT2_S_IFREG= 0x8000,	/* regular file */
	EXT2_S_IFBLK= 0x6000,	/* block device */
	EXT2_S_IFDIR= 0x4000,	/* directory */
	EXT2_S_IFCHR= 0x2000,	/* character device */
	EXT2_S_IFIFO= 0x1000,	/* fifo */
	EXT2_S_ISUID= 0x0800,	/* SUID */
	EXT2_S_ISGID= 0x0400,	/* SGID */
	EXT2_S_ISVTX= 0x0200,	/* sticky bit */
	EXT2_S_IRWXU= 0x01C0,	/* user access rights mask */
	EXT2_S_IRUSR= 0x0100,	/* read */
	EXT2_S_IWUSR= 0x0080,	/* write */
	EXT2_S_IXUSR= 0x0040,	/* execute */
	EXT2_S_IRWXG= 0x0038,	/* group access rights mask */
	EXT2_S_IRGRP= 0x0020,	/* read */
	EXT2_S_IWGRP= 0x0010,	/* write */
	EXT2_S_IXGRP= 0x0008,	/* execute */
	EXT2_S_IRWXO= 0x0007,	/* others access rights mask */
	EXT2_S_IROTH= 0x0004,	/* read */
	EXT2_S_IWOTH= 0x0002,	/* write */
	EXT2_S_IXOTH= 0x0001,	/* execute */

	/* behaviour control flags at "i_flags". */
	EXT2_SECRM_FL=0x00000001,		/* secure deletion */
	EXT2_UNRM_FL=0x00000002,		/* record for undelete */
	EXT2_COMPR_FL=0x00000004,		/* compressed file */
	EXT2_SYNC_FL=0x00000008,		/* synchronous updates */
	EXT2_IMMUTABLE_FL=0x00000010,	/* immutable file */
	EXT2_APPEND_FL=0x00000020,		/* append only */
	EXT2_NODUMP_FL=0x00000040,		/* do not dump/delete file */
	EXT2_NOATIME_FL=0x00000080,		/* do not update .i_atime */
	EXT2_DIRTY_FL=0x00000100,		/* dirty (file is in use?) */
	EXT2_COMPRBLK_FL=0x00000200,	/* compressed blocks */
	EXT2_NOCOMPR_FL=0x00000400,		/* access raw compressed data */
	EXT2_ECOMPR_FL=0x00000800,		/* compression error */
	EXT2_BTREE_FL=0x00010000,		/* b-tree format directory */
	EXT2_INDEX_FL=0x00010000,		/* Hash indexed directory */

	EXT2_ROOT_INODE_INDEX=2,		/* ルートinodeのインデックス。 */

	/* indirect data block index. */
	INODE_DATA_INDIRECT1=12,		/* 間接参照ブロック配列値。 */
	INODE_DATA_INDIRECT2=13,		/* 二重間接参照ブロック配列値。 */
	INODE_DATA_INDIRECT3=14,		/* 三重間接参照ブロック配列値。 */

	INODE_DATA_BLOCK_NUM=15,		/* データブロック数。 */
	INODE_DIRECT_BLOCK_NUM = INODE_DATA_INDIRECT1,	/* 直接ブロック数 */

	INODE_BLOCK_SIZE=512,			/* inodeのi_blocksのブロック単位バイト数。 */
};


typedef struct{
	ushort i_mode;			/* 00,format of the described file and the access rights. */
	ushort i_uid;			/* 02,user id. */
	uint i_size;			/* 04,size of the file in bytes. */
	uint i_atime;			/* 08,Unix time,last time this file was accessed. */
	uint i_ctime;			/* 0c,Unix time,when file was created. */
	uint i_mtime;			/* 10,Unix time,last time this file was modified. */
	uint i_dtime;			/* 14,Unix time,when file was deleted. */
	ushort i_gid;			/* 18,group id. */
	ushort i_links_count;	/* 1a,how many times this inode is linked. */
	uint i_blocks;			/* 1c,both currently in used and currently reserved blocks.number of 512 bytes block. */
	uint i_flags;			/* 20,behave when accessing the data for this inode. */
	uint i_number;			/* 24,OS dependent 1,inode number(open時に入れる) */
	/*
	 * data block number.
	 * 1〜12 direct block number.
	 * 13 indirect block number.
	 * 14 bi-indirect block number.
	 * 15 tri-indirect block number.
	 * Each indirect block array contains as many entries of 32bit block numbers as possible
	 * (to fill one entire block).
	 */
	uint i_block[INODE_DATA_BLOCK_NUM];		/* 28 */
	uint i_generation;		/* 64,file version (used by NFS). */
	uint i_file_acl;		/* 68,block number containing the extended attributes, */
	uint i_dir_acl;			/* 6c,"high size" of the file. */
	uint i_faddr;			/* 70,location of the last file fragment. */
	uint i_osd2[3];			/* 74,OS dependent 2 */
}INODE;

typedef struct{
	const char *path;
	INODE *inodeData;
	INODE *inodeBuf;
}INODE_PARAM;


/*====================================================================================================*
 *                                       read data                                                    *
 *====================================================================================================*/

typedef int (*READ_DATA)(int,void*,uint*,size_t,size_t,int);


static int readDirectBlock(int,void*,uint*,size_t,size_t,int);
static int readIndirectBlock(int,void*,uint*,size_t,size_t,int);


/* PRIVATE */
static READ_DATA readIndirect[]={readDirectBlock,readIndirectBlock,readIndirectBlock};


/*
 * PRIVATE
 * データ読込み。
 * parameters : device inode,buffer,block table,ブロックテーブル内の書込みサイズ,ブロックテーブル内の開始バイト数,node
 * return : 書込みサイズ or error number
 */
STATIC int readDirectBlock(int din,void *buf,uint *block_tbl,size_t size,size_t begin,int node)
{
	char *tmp_buf=NULL;
	int block_size=superBlock[din]->blockSize;
	int sectors=superBlock[din]->sectors;
	int rest_size=size;
	int rest;
	int s,t;


	block_tbl+=begin/block_size;

	/* 開始バイト値がブロック境界でない場合。 */
	if(begin%block_size>0)
	{
		s=begin%block_size;
		t=(rest_size>block_size-s)?block_size-s:rest_size;
		if((tmp_buf=(char*)kmalloc(block_size))==NULL)return -ENOMEM;
		if((rest=read_cache(din,tmp_buf,sectors,blkToSect(din,*block_tbl)))<0)goto ERR;
		memcpy(buf,tmp_buf+s,t);
		buf=(char*)buf + t;
		rest_size-=t;
		++block_tbl;
	}

	for(;rest_size>=block_size;rest_size-=block_size)
	{
		if((rest=read_cache(din,buf,sectors,blkToSect(din,*block_tbl)))<0)goto ERR;
		buf=(char*)buf + block_size;
		++block_tbl;
	}

	/* 最後のバイト値がブロック境界でない場合。 */
	if(rest_size>0)
	{
		if(tmp_buf==NULL)
			if((tmp_buf=(char*)kmalloc(block_size))==NULL)return -ENOMEM;
		if((rest=read_cache(din,tmp_buf,sectors,blkToSect(din,*block_tbl)))<0)goto ERR;
		memcpy(buf,tmp_buf,rest_size);
	}

	kfree(tmp_buf);

	return size;

ERR:
	kfree(tmp_buf);

	return rest;
}


/*
 * PRIVATE
 * 間接読込み。
 * parameters : デバイスinode,バッファ,ブロックテーブル,ブロックテーブル内の読込みサイズ,ブロックテーブル内の開始バイト数,node
 * return : 書込みサイズ or error number
 */
STATIC int readIndirectBlock(int din,void *buf,uint *block_tbl,size_t size,size_t begin,int node)
{
	uint *block_buf;
	uint rest_size=size;
	int sectors=superBlock[din]->sectors;
	int block_size=superBlock[din]->blockSize;
	int idrct_block_log=EXT2_BLOCK_BASE_LOG+superBlock[din]->s_log_block_size-UINT_LOG;
	int idrct_size=block_size<<(idrct_block_log*node);
	int rest;
	int s,t;


	if((block_buf=(uint*)kmalloc(block_size))==NULL)return -ENOMEM;

	block_tbl+=begin/idrct_size;

	/* 開始バイト値がブロック境界でない場合。 */
	if(begin%idrct_size>0)
	{
		s=begin%idrct_size;
		t=(rest_size>idrct_size-s)?idrct_size-s:rest_size;
		if((rest=read_cache(din,block_buf,sectors,blkToSect(din,*block_tbl)))<0)goto ERR;
		if((rest=readIndirect[node-1](din,buf,block_buf,t,s,node-1))<0)goto ERR;
		buf=(char*)buf + t;
		rest_size-=t;
		++block_tbl;
	}

	for(;rest_size>=idrct_size;rest_size-=idrct_size)
	{
		if((rest=read_cache(din,block_buf,sectors,blkToSect(din,*block_tbl)))<0)goto ERR;
		if((rest=readIndirect[node-1](din,buf,block_buf,idrct_size,0,node-1))<0)goto ERR;
		buf=(char*)buf + idrct_size;
		++block_tbl;
	}

	if(rest_size>0)
	{
		if((rest=read_cache(din,block_buf,sectors,blkToSect(din,*block_tbl)))<0)goto ERR;
		if((rest=readIndirect[node-1](din,buf,block_buf,rest_size,0,node-1))<0)goto ERR;
	}

	kfree(block_buf);

	return size;
ERR:
	kfree(block_buf);

	return rest;
}


/*
 * PUBLIC
 * ファイルデータを読み込む。
 * parameters : device inode,inode,buffer,read size,begin byte
 * return : read size or error number
 */
STATIC int readData(int din,INODE *inode,void *buf,size_t size,size_t begin)
{
	uint64 nsize;
	uint *table;
	size_t rest_size=size;
	int sectors=superBlock[din]->sectors;
	int block_size=superBlock[din]->blockSize;
	int nblock=block_size/sizeof(uint);			/* ブロックテーブルのブロック数。 */
	int rest;
	int read_size;
	int i;

	nsize=INODE_DATA_INDIRECT1*block_size;	/* 直接ブロックサイズ。 */
	if(begin<nsize)goto DIRECT;
	begin-=nsize;
	nsize=block_size*nblock;				/* 間接ブロックサイズ。 */
	for(i=INODE_DATA_INDIRECT1;;++i)
	{
		if(begin<nsize)goto INDIRECT;
		begin-=nsize;
		nsize*=nblock;
	}

DIRECT:
	/* 直接ブロック。 */
	read_size=(rest_size>nsize-begin)?nsize-begin:rest_size;
	if((rest=readDirectBlock(din,buf,inode->i_block,read_size,begin,0))<0)return rest;
	if((rest_size-=read_size)==0)return size;
	buf=(char*)buf + read_size;
	begin=0;

	/* 間接ブロック。 */
	nsize=block_size*nblock;				/* 間接ブロックサイズ。 */
	i=INODE_DATA_INDIRECT1;
INDIRECT:
	if((table=(uint*)kmalloc(block_size))==NULL)return -ENOMEM;
	for(;;++i)
	{
		if((rest=read_cache(din,table,sectors,blkToSect(din,inode->i_block[i])))<0)goto ERR;
		read_size=(rest_size>nsize-begin)?nsize-begin:rest_size;
		if((rest=readIndirect[i-INODE_DATA_INDIRECT1](din,buf,table,read_size,begin,i-INODE_DATA_INDIRECT1))<0)goto ERR;
		if((rest_size-=read_size)==0)break;
		buf=(char*)buf + read_size;
		begin=0;
		nsize*=nblock;
	}

	kfree(table);

	return size;
ERR:
	kfree(table);

	return rest;
}


/*====================================================================================================*
 *                                      write data                                                    *
 *====================================================================================================*/


typedef struct{
	uint *buf[3];	/* 0:間接ブロック 1:２重間接ブロック 2:３重間接ブロック */
	uint block[3];	/* ブロックナンバー */
	uint begin;		/* 間接ブロックの書き込む始めのインデックス */
	uint last;		/* 間接ブロックのインデックス数 */
}WRITE_BLOCK;


//================================== PRIVATE ============================================


/*
 * １ブロックテーブルあたりのブロック数
 * 0:1024ブロックサイズ時 1:4096ブロックサイズ時
 */
static int blkNum[2][3] = {{256, 256  * 256, 256  * 256  * 256},
   	                       {1024,1024 * 1024,1024 * 1024 * 1024}};


// ４Kバイトブロックか？
STATIC INLINE int is4Kbyte(int din)
{
	return superBlock[din]->blockSize / 4096;
}

STATIC INLINE int divBlk(int din,int node,size_t startblk)
{
	return startblk / blkNum[is4Kbyte(din)][node];
}

STATIC INLINE int surpBlk(int din,int node,size_t startblk)
{
	return startblk % blkNum[is4Kbyte(din)][node];
}


// WRITE_BLOCK構造体と間接ブロックにブロックを割り当てる
// parameters : device inode, 相対スタート位置, 間接ノード, 該当ブロック番号
// return : 0 ro error number
STATIC int getIndirectBlock(int din, size_t startBlk, int node, uint *m_blk, WRITE_BLOCK *o_blkStruct)
{
	int rest;


	// ブロックバッファの割り当て
	if (o_blkStruct->buf[node] == NULL){
		if ((o_blkStruct->buf[node] = kmalloc(superBlock[din]->blockSize)) == NULL){
			return -ENOMEM;
		}
	}

	// 該当ブロックの割当が無かったら割り当てる。
	if (*m_blk == 0){
		/* 現在のブロックバッファを書き戻す */
		if (o_blkStruct->block[node] != 0){
			rest = write_cache(din, o_blkStruct->buf[node], superBlock[din]->sectors, blkToSect(din, o_blkStruct->block[node]));
			if (rest < 0){
				return rest;
			}
		}

		/* 新しいブロックを割り当てる */
		rest = getBlock(din);
		if (rest < 0){
			return rest;
		}
		o_blkStruct->block[node] = *m_blk = rest;
		memset(o_blkStruct->buf[node], 0, superBlock[din]->blockSize);
		rest = write_cache(din, o_blkStruct->buf[node], superBlock[din]->sectors, blkToSect(din, *m_blk));
		if (rest < 0){
			return rest;
		}
	}
	else if (*m_blk != o_blkStruct->block[node]){
		/* 現在のブロックバッファを書き戻す */
		if (o_blkStruct->block[node] != 0){
			rest = write_cache(din, o_blkStruct->buf[node], superBlock[din]->sectors, blkToSect(din, o_blkStruct->block[node]));
			if (rest < 0){
				return rest;
			}
		}

		/* ブロックを読み込む */
		rest = read_cache(din, o_blkStruct->buf[node], superBlock[din]->sectors, blkToSect(din, *m_blk));
		if (rest < 0){
			return rest;
		}
		o_blkStruct->block[node] = *m_blk;
	}
	
	if (0 < node){
		return getIndirectBlock(
			din, surpBlk(din, node - 1, startBlk), node - 1, &o_blkStruct->buf[node][divBlk(din, node - 1, startBlk)], o_blkStruct);
	}
	
	return 0;
}


/*
 * ブロックテーブルをWRITE_BLOCK構造体にセットする
 * WRITE_BLOCK構造体は正しい値以外は初期化されていることが前提
 */
STATIC int getBlockTable(int din, INODE *inode, size_t start, WRITE_BLOCK *o_blkStruct)
{
	size_t startBlk = start;
	int rest;
	
	// すでに割り当てられているテーブルがinodeなら一旦クリアーする
	if (o_blkStruct->buf[0] == (uint*)inode){
		o_blkStruct->buf[0] = NULL;
	}

	if (startBlk < INODE_DIRECT_BLOCK_NUM){
		if (o_blkStruct->buf[0] != NULL){
			kfree(o_blkStruct->buf[0]);
		}
		o_blkStruct->buf[0] = (uint*)inode;
		o_blkStruct->block[0] = 0;
		o_blkStruct->begin = startBlk + OFFSETOF(INODE, i_block[0]) / sizeof(uint);
		o_blkStruct->last = OFFSETOF(INODE,i_block[INODE_DATA_INDIRECT1]) / sizeof(uint);
	}
	else if ((startBlk -= INODE_DIRECT_BLOCK_NUM)  <  blkNum[is4Kbyte(din)][0]){
		rest = getIndirectBlock(din, startBlk, 0, &inode->i_block[INODE_DATA_INDIRECT1], o_blkStruct);
		if (rest < 0){
			return rest;
		}
		o_blkStruct->begin = startBlk;
		o_blkStruct->last = blkNum[is4Kbyte(din)][0];
	}
	else if ((startBlk -= blkNum[is4Kbyte(din)][0]) < blkNum[is4Kbyte(din)][1]){
		rest = getIndirectBlock(din, startBlk, 1, &inode->i_block[INODE_DATA_INDIRECT2], o_blkStruct);
		if (rest < 0){
			return rest;
		}
		o_blkStruct->begin = startBlk % blkNum[is4Kbyte(din)][0];
		o_blkStruct->last = blkNum[is4Kbyte(din)][0];
	}
	else if ((startBlk -= blkNum[is4Kbyte(din)][1]) < blkNum[is4Kbyte(din)][2]){
		rest = getIndirectBlock(din, startBlk, 2, &inode->i_block[INODE_DATA_INDIRECT3], o_blkStruct);
		if (rest < 0){
			return rest;
		}
		o_blkStruct->begin = startBlk % blkNum[is4Kbyte(din)][0];
		o_blkStruct->last = blkNum[is4Kbyte(din)][0];
	}
	else{
		return -EFBIG;
	}
	
	return 0;
}


/***************************** パブリックメソッド *****************************/


/*
 * データブロックに書き込む。
 * parameters : device inode,inode,buffer,read size,begin byte
 * return : 0 or error number
 */
STATIC int writeData(int din, INODE *inode, void *buf, size_t lenByte, size_t beginByte)
{
	char *data = buf;
	char *dataBuf = NULL;
	int ext2BlkSize = superBlock[din]->blockSize;
	int blkSects = superBlock[din]->sectors;
	size_t startByte;
	size_t allLastByte;
	size_t startBlk;
	size_t lastBlk;
	size_t allLastBlk;
	WRITE_BLOCK blkStruct;
	int rest, c_blk;

	// 初期化
	memset(&blkStruct, 0, sizeof(WRITE_BLOCK));

	// 現在の最後のブロックから、書き込む最初までの未割り当てブロックを割り当てる
	if (inode->i_size < beginByte){
		startBlk = ROUNDUP_DIV(inode->i_size, ext2BlkSize);
		allLastBlk = ROUNDUP_DIV(beginByte, ext2BlkSize);
		for (; startBlk < allLastBlk;){
			rest = getBlockTable(din, inode, startBlk, &blkStruct);
			if (rest < 0){
				goto END;
			}
			lastBlk = MIN(allLastBlk - startBlk + blkStruct.begin, blkStruct.last);
			for (c_blk = blkStruct.begin; c_blk < lastBlk; ++c_blk){
				ASSERT(blkStruct.buf[0][c_blk] == 0);

				rest = getBlock(din);
				if (rest < 0){
					goto END;
				}
				blkStruct.buf[0][c_blk] = rest;
			}
			startBlk += lastBlk - blkStruct.begin;
		}
	}

	// ブロックテーブルごとにデータを書き込む
	for (startByte = beginByte, allLastByte = lenByte + beginByte; startByte < allLastByte;){
		rest = getBlockTable(din, inode, startByte / ext2BlkSize, &blkStruct);
		if (rest < 0){
			goto END;
		}
		c_blk = blkStruct.begin;

		// 最初のデータがブロックの途中から始まるときは、一旦ブロックを読み込んでから書き込む
		if (startByte % ext2BlkSize){
			int blkOffset, copyLen;
	
			ASSERT(blkStruct.buf[0][c_blk] != 0);

			dataBuf = kmalloc(ext2BlkSize);
			if (dataBuf == NULL){
				return -ENOMEM;
			}
			rest = read_cache(din, dataBuf, blkSects, blkToSect(din, blkStruct.buf[0][c_blk]));
			if (rest < 0){
				goto END;
			}
			blkOffset = startByte % ext2BlkSize;
			copyLen = MIN(allLastByte - startByte, ext2BlkSize - blkOffset);
			memcpy(dataBuf + blkOffset, data, copyLen);
			data += copyLen;
			rest = write_cache(din, dataBuf, blkSects, blkToSect(din, blkStruct.buf[0][c_blk]));
			if (rest < 0){
				goto END;
			}
			++c_blk;
			
			// データの最後まで書き込んでいたら終了
			if (copyLen == allLastByte - startByte){
				break;
			}
		}
	
		lastBlk = MIN((allLastByte / ext2BlkSize) - (startByte / ext2BlkSize) + blkStruct.begin, blkStruct.last);
		for (; c_blk < lastBlk; ++c_blk){
			if (blkStruct.buf[0][c_blk] == 0){
				rest = getBlock(din);
				if (rest < 0){
					goto END;
				}
				blkStruct.buf[0][c_blk] = rest;
			}
			rest = write_cache(din, data, blkSects, blkToSect(din, blkStruct.buf[0][c_blk]));
			if (rest < 0){
				goto END;
			}
			data += ext2BlkSize;
		}
		
		// 最後のデータがブロックの途中で終わりかつブロックが割り当ててあるときは、
		// ブロック読み込んでからデータを書き込む
		if ((lastBlk < blkStruct.last) && (allLastByte % ext2BlkSize)){
			if (dataBuf == NULL){
				if ((dataBuf = kmalloc(ext2BlkSize)) == NULL){
					return -ENOMEM;
				}
			}
			if (blkStruct.buf[0][lastBlk] == 0){
				if ((rest = getBlock(din)) < 0){
					goto END;
				}
				blkStruct.buf[0][lastBlk] = rest;
			}
			else{
				rest = read_cache(din, dataBuf, blkSects, blkToSect(din, blkStruct.buf[0][lastBlk]));
				if (rest < 0){
					goto END;
				}
			}
			memcpy(dataBuf, data, allLastByte % ext2BlkSize);
			rest = write_cache(din, dataBuf, blkSects, blkToSect(din, blkStruct.buf[0][lastBlk]));
			if (rest < 0){
				goto END;
			}

			break;
		}
		
		startByte = ROUNDDOWN(startByte, ext2BlkSize) + (lastBlk - blkStruct.begin) * ext2BlkSize;
	}
	rest = 0;
END:
	for (c_blk = 0; c_blk < 3; ++c_blk){
		int rest_tmp;
		
		if (blkStruct.block[c_blk] != 0){
			rest_tmp = write_cache(din, blkStruct.buf[c_blk], blkSects, blkToSect(din, blkStruct.block[c_blk]));
			if (rest_tmp < 0){
				rest = rest_tmp;
			}
			kfree(blkStruct.buf[c_blk]);
		}
	}
	kfree(dataBuf);

	return rest;
}


/*====================================================================================================*
 *                                       release                                                      *
 *====================================================================================================*/

typedef int (*RELEASE_DATA)(int,uint*,int,int);


static int releaseDirect(int,uint*,int,int);
static int releaseIndirect(int,uint*,int,int);
RELEASE_DATA releaseData[]={releaseDirect,releaseIndirect,releaseIndirect,releaseIndirect};


/*
 * PRIVATE
 * 直接ブロックテーブルのブロックを開放する。
 * parameters : device inode,block table,number of blocks,node=0
 * return : 0
 */
STATIC int releaseDirect(int din, uint *table, int blk_num, int node)
{
	int i;

	for (i = 0; i < blk_num; ++i){
		releaseBlock(din, table[i]);
	}

	return 0;
}


/*
 * PRIVATE
 * 間接ブロックテーブルのブロックを開放する。
 * parameters : device inode,block table,number of blocks,node
 * return : 0 or error number
 */
STATIC int releaseIndirect(int din, uint *table, int blk_num, int node)
{
	uint *blkBuf;
	int sectors = superBlock[din]->sectors;
	int nblk = 1 << ((EXT2_BLOCK_BASE_LOG + superBlock[din]->s_log_block_size - INT_LOG) * node);
	int rest;
	int i;

	blkBuf = kmalloc(superBlock[din]->blockSize);
	if (blkBuf == NULL){
		return -ENOMEM;
	}

	i = 0;
	for(; nblk < blk_num; blk_num -= nblk)
	{
		rest = read_cache(din, blkBuf, sectors, blkToSect(din, table[i]));
		if (rest < 0){
			goto END;
		}
		rest = releaseData[node - 1](din, blkBuf, nblk, node - 1);
		if (rest < 0){
			goto END;
		}
		releaseBlock(din, table[i++]);
	}
	rest = read_cache(din, blkBuf, sectors, blkToSect(din, table[i]));
	if (rest < 0){
		goto END;
	}
	rest = releaseData[node - 1](din, blkBuf, blk_num, node - 1);
	if (rest < 0){
		goto END;
	}
	releaseBlock(din, table[i]);

	rest = 0;
END:
	kfree(blkBuf);

	return rest;
}


/*
 * PUBLIC
 * データブロックを開放する。
 * return : 0 or error number
 */
STATIC int releaseDataBlock(int din, INODE *inode)
{
	uint *blkBuf;
	int sectors = superBlock[din]->sectors;
	int block_size = superBlock[din]->blockSize;
	int blk_num = ROUNDUP_DIV(inode->i_size, block_size);
	int nblk;
	int rest;
	int blk, i;

	// 直接ブロック
	blk = (INODE_DATA_INDIRECT1 <= blk_num)? INODE_DATA_INDIRECT1 : blk_num;
	releaseDirect(din, inode->i_block, blk, 0);
	for (i = 0; i < blk; ++i){
		inode->i_block[i] = 0;
	}
	if ((blk_num -= blk) == 0){
		goto END;
	}

	// 間接ブロック
	blkBuf = kmalloc(block_size);
	if (blkBuf == NULL){
		return -ENOMEM;
	}
	blk = nblk = block_size / sizeof(uint);
	for (i = INODE_DATA_INDIRECT1;; ++i)
	{
		rest = read_cache(din, blkBuf, sectors, blkToSect(din,inode->i_block[i]));
		if (rest < 0){
			goto ERR;
		}
		rest = releaseData[i - INODE_DATA_INDIRECT1](din, blkBuf, (blk < blk_num)? blk : blk_num, i - INODE_DATA_INDIRECT1);
		if (rest < 0){
			goto ERR;
		}
		releaseBlock(din, inode->i_block[i]);
		inode->i_block[i] = 0;

		blk_num -= blk;
		if (blk_num <= 0){
			break;
		}
		blk *= nblk;
	}
	kfree(blkBuf);
END:
	inode->i_size = 0;

	return 0;
ERR:
	kfree(blkBuf);

	return rest;
}


/*====================================================================================================*
 *                                          misc                                                      *
 *====================================================================================================*/

/* PRIVATE. */
static BITMAP inodeBm[MAX_DEVICE_OPEN];


/*
 * PUBLIC
 * inode番号から、inodeを引数のブロックバッファに読み込む。
 * parameters : device inode,inode number,block buffer
 * return : inode addres or error number;
 */
STATIC INLINE int readInode(int din,uint inumber,INODE *sector_buf)
{
	int gdesc;
	int index;
	uint sector;


	gdesc = (inumber - 1) / superBlock[din]->s_inodes_per_group;
	index = (inumber - 1) % superBlock[din]->s_inodes_per_group;
	sector = blkToSect(din,grpDesc[din][gdesc].bg_inode_table) + index * sizeof(INODE) / superBlock[din]->sectorSize;
	if (read_cache(din,sector_buf,1,sector) != 1)
		return -EIO;

	return (int)sector_buf + index * sizeof(INODE) % superBlock[din]->sectorSize;
}


/*
 * PUBLIC
 * inodeをディスクに書き込む。
 * parameters : device inode,inode number,block buffer
 * return : 0 or error number
 */
STATIC INLINE int writeInode(int din,uint inumber,INODE *sector_buf)
{
	int gdesc;
	int index;
	uint sector;
	int rest;


	gdesc = (inumber - 1) / superBlock[din]->s_inodes_per_group;
	index = (inumber - 1) % superBlock[din]->s_inodes_per_group;
	sector = blkToSect(din,grpDesc[din][gdesc].bg_inode_table) + index * sizeof(INODE) / superBlock[din]->sectorSize;
	rest = write_cache(din,sector_buf,1,sector);
	if (rest < 0){
		return rest;
	}

	return 0;
}


/*
 * PUBLIC
 * 空きinode番号を取得する。
 * parameters : device inode,file type
 * return : inode number or error=0
 */
STATIC uint getNewInode(int din,int type)
{
	GROUP_DESC *gd=grpDesc[din];
	SUPER_BLOCK *sb=superBlock[din];
	uint inode;
	int i,last;


	/* 現在のグループに空きinodeがない。 */
	if (gd[inodeBm[din].currentGd].bg_free_inodes_count == 0){
		int sectors=sb->sectors;

		/* 現在のビットマップを書き戻す。 */
		if (write_cache(din,inodeBm[din].bitmap,sectors,blkToSect(din,gd[inodeBm[din].currentGd].bg_inode_bitmap)) != sectors){
			return 0;
		}

		for (i = inodeBm[din].currentGd + 1;; ++i)
		{
			if (i == grpDescNum[din]){
				i = 0;
			}
			if (i == inodeBm[din].currentGd){
				return 0;
			}
			if (gd[i].bg_free_inodes_count != 0){
				break;
			}
		}
		if (read_cache(din,inodeBm[din].bitmap,sectors,blkToSect(din,gd[i].bg_inode_bitmap))!=sectors){
			return 0;
		}
		inodeBm[din].currentGd = i;
	}

	/* 空きビットマップの検索。 */
	i=inodeBm[din].currentByte;
	last=sb->s_inodes_per_group/BYTE_BIT;
	while(inodeBm[din].bitmap[i]==0xff)
		if(++i>=last)i=0;
	inodeBm[din].currentByte=i;
	inode=i*BYTE_BIT+emptyBit[inodeBm[din].bitmap[i]];
	inodeBm[din].bitmap[i]|=1<<emptyBit[inodeBm[din].bitmap[i]];

	/* グループディスクリプタとスーパーブロックを更新する。 */
	--gd[inodeBm[din].currentGd].bg_free_inodes_count;
	--sb->s_free_inodes_count;
	
	/* グループディスクリプタのディレクトリカウントを増やす */
	if ((type & EXT2_S_IFMT) == EXT2_S_IFDIR)
		grpDesc[din][inodeBm[din].currentGd].bg_used_dirs_count += 1;

	return inode+inodeBm[din].currentGd*sb->s_inodes_per_group+1;
}


/*
 * PUBLIC
 * inodeビットマップの開放、グループディスクリプタの変更
 * parameters : device inode,inode number
 * return : 0 or error number
 */
STATIC int releaseInodeBitAndGrp(int din,int inumber,int type)
{
	uchar *bitmap;
	int grp;
	SUPER_BLOCK *sb=superBlock[din];
	int sectors=sb->sectors;
	int a,rest;


	/* ブロックビットマップの検索。 */
	grp = (inumber - 1) / (sb->s_inodes_per_group);
	if (grp != inodeBm[din].currentGd)
	{
		if ((bitmap = kmalloc(sb->blockSize)) == NULL)
			return -ENOMEM;
		if ((rest = read_cache(din,bitmap,sectors,blkToSect(din,grpDesc[din][grp].bg_inode_bitmap))) != sectors)
			return rest;
	}
	else bitmap = inodeBm[din].bitmap;

	/* ビットマップに書き込む。 */
	a = inumber - grp * sb->s_inodes_per_group - 1;
	bitmap[a / BYTE_BIT] &= ~(1 << a % BYTE_BIT);
	if (grp != inodeBm[din].currentGd)
	{
		/* 現在のブロックグループと一致するか。 */
		if (grp == blockBm[din].currentGd)
		{
			uchar *buf;

			buf = inodeBm[din].bitmap;
			inodeBm[din].bitmap = bitmap;
			bitmap = buf;
			inodeBm[din].currentGd = grp;
			inodeBm[din].currentByte = a / BYTE_BIT;
		}
		if ((rest = write_cache(din,bitmap,sectors,blkToSect(din,grpDesc[din][grp].bg_inode_bitmap))) != sectors)
			return rest;
		kfree(bitmap);
	}

	/* ブロックカウントを増やす。 */
	++grpDesc[din][grp].bg_free_inodes_count;
	++sb->s_free_inodes_count;

	/* ディレクトリならディレクトリカウントを減らす。 */
	if ((type & EXT2_S_IFMT) == EXT2_S_IFDIR)
		--grpDesc[din][grp].bg_used_dirs_count;

	return 0;
}


/*
 * PUBLIC
 * 通常ファイルならinodeリンクカウントを減らしてリンク０なら削除する
 * ディレクトリならリンクカウント２のとき削除する
 * parameters : device inode,inode
 * return : number of links or errnor number
 */
STATIC int unlinkInode(int din,INODE *inode)
{
	INODE *sect_buf;
	int rest;
	
	/* 通常ファイル */
	if ((inode->i_mode & EXT2_S_IFMT) == EXT2_S_IFREG){
		/* リンクカウントを減らす */
		inode->i_links_count -= 1;

		ASSERT(0 <= (short)inode->i_links_count);
	}
	/* ディレクトリ */
	else if ((inode->i_mode & EXT2_S_IFMT) == EXT2_S_IFDIR){
		ASSERT(2 <= inode->i_links_count);

		/* リンクカウント2より大きければ終わり */
		if (2 < inode->i_links_count){
			return inode->i_links_count;
		}

		inode->i_links_count = 0;
	}
	/* その他のファイルタイプは未対応 */
	else{
		return -EOPNOTSUPP;
	}

	/* リンクカウントが0なら削除 */
	if (inode->i_links_count == 0){
		/* データブロックを開放する。 */
		rest = releaseDataBlock(din,inode);
		if (rest < 0){
			return rest;
		}

		/* inode領域を開放する */
		rest = releaseInodeBitAndGrp(din,inode->i_number,inode->i_mode);
		if (rest < 0){
			return rest;
		}

		/* delete timeを設定する */
		inode->i_dtime = (uint)sys_time(NULL);
	}

	/* inodeを書き戻す */
	sect_buf = kmalloc(superBlock[din]->sectorSize);
	if (sect_buf == NULL){
		return -ENOMEM;
	}
	rest = readInode(din,inode->i_number,sect_buf);
	if (rest < 0){
		return rest;
	}
	memcpy((void*)rest,inode,sizeof(INODE));
	rest = writeInode(din,inode->i_number,sect_buf);
	if (rest < 0){
		return rest;
	}
	kfree(sect_buf);

	return inode->i_links_count;
}


/*
 * PUBLIC
 * マウント時の初期化。
 * parameters : device inode,検索を開始するブロックグループナンバー
 * return : 0 or error nomber
 */
STATIC int initInode(int din,int begin_gr)
{
	GROUP_DESC *gd=grpDesc[din];
	SUPER_BLOCK *sb=superBlock[din];
	INODE *inode,*sectBuf;
	int i,rest;


	/* inodeビットマップを読み込む。 */
	inodeBm[din].bitmap = kmalloc(sb->blockSize);
	if (inodeBm[din].bitmap == NULL)
		return -ENOMEM;
	inodeBm[din].currentGd = begin_gr;
	inodeBm[din].currentByte = 0;

	i = begin_gr;
	do
	{
		if (gd[i].bg_free_inodes_count > 0)
		{
			rest = read_direct(din,inodeBm[din].bitmap,sb->sectors,blkToSect(din,gd[i].bg_inode_bitmap));
			if (rest != sb->sectors)
				return rest;
			inodeBm[din].currentGd = i;
			for (i = 0; inodeBm[din].bitmap[i] == 0xff; ++i);
			inodeBm[din].currentByte = i;
			break;
		}
		if (++i == grpDescNum[din]){
			i = 0;
		}
	}while (i != begin_gr);

	/* ルートinodeの初期化。 */
	sectBuf = kmalloc(superBlock[din]->sectorSize);
	if (sectBuf == NULL)
		return -ENOMEM;
	rest = readInode(din,EXT2_ROOT_INODE_INDEX,sectBuf);
	if (rest < 0)
		goto ERR;
	inode = (INODE*)rest;
	inode->i_number = EXT2_ROOT_INODE_INDEX;
	rest = writeInode(din,EXT2_ROOT_INODE_INDEX,sectBuf);
	if (rest < 0)
		goto ERR;
	kfree(sectBuf);

	return 0;

ERR:
	kfree(sectBuf);

	return rest;
}


/*
 * PUBLIC
 * アンマウント時の処理。
 * parameters : device inode
 */
STATIC int umountInode(int din)
{
	/* inodeビットマップをディスクに書き戻す。 */
	write_cache(din,inodeBm[din].bitmap,superBlock[din]->sectors,blkToSect(din,grpDesc[din][inodeBm[din].currentGd].bg_inode_bitmap));
	kfree(inodeBm[din].bitmap);

	return 0;
}


/********************************************************************************************************
 *
 * Directory
 *
 ********************************************************************************************************/

enum{
	EXT2_NAME_LEN = 255,

	/* directory file types. */
	EXT2_DIR_UNKNOWN = 0,
	EXT2_DIR_REG_FILE,
	EXT2_DIR_DIR,
	EXT2_DIR_CHRDEV,
	EXT2_DIR_BLKDEV,
	EXT2_DIR_FIFO,
	EXT2_DIR_SOCK,
	EXT2_DIR_SYMLINK,
	EXT2_DIR_MAX,
};


typedef struct{
	uint inode;				/* inode number. */
	ushort rec_len;			/* directory entry length. */
	uchar name_len;			/* name length. */
	uchar file_type;		/* file type. */
	char name[0];			/* File name. */
}DIR_ENTRY;

typedef struct{
	const char *path;
	DIR_ENTRY *dirBuf;
	uint blkNumber;
}DIR_PARAM;

typedef int (*READ_DIR)(int,uint*,int,DIR_PARAM*,int,int (**READ_DIR)());


#define ROUNDUP4(a) (((a+3)>>2)<<2)		/* 4で繰り上げ。 */


/***************************** プライベートメソッド ****************************/


/*
 * search entry.
 * parameters : device inode,path,entry buffer
 * return : directory entry address or failed=NULL
 */
STATIC DIR_ENTRY *searchEntry(int din,const char *path,DIR_ENTRY *entry)
{
	DIR_ENTRY *last;


	last=(DIR_ENTRY*)((char*)entry+superBlock[din]->blockSize);
	if (entry->inode == 0)
		entry=(DIR_ENTRY*)((char*)entry + entry->rec_len);

	for(;entry<last;entry=(DIR_ENTRY*)((char*)entry + entry->rec_len))
		if (cmpPath(entry->name,path,entry->name_len)==0)return entry;

	return NULL;
}


/*
 * 空きエントリーを探す。
 * parameters : device inode,directory block,directory parameters struct
 * return : directory entry address or failed=NULL
 */
STATIC DIR_ENTRY *searchEntrySpace(int din,DIR_PARAM *dparam)
{
	int add_len,ent_len;
	DIR_ENTRY *entry,*last,*new;


	add_len=strlen(dparam->path)+sizeof(DIR_ENTRY);
	entry=dparam->dirBuf;
	last=(DIR_ENTRY*)((char*)dparam->dirBuf+superBlock[din]->blockSize);

	if(entry->inode == 0)
	{
		if(entry->rec_len>=add_len)
		{
			entry->name_len=add_len-sizeof(DIR_ENTRY);
			return entry;
		}
		else entry=(DIR_ENTRY*)((char*)entry + entry->rec_len);
	}

	for(;entry<last;entry=(DIR_ENTRY*)((char*)entry + entry->rec_len))
	{
		ent_len=ROUNDUP4(entry->name_len+sizeof(DIR_ENTRY));
		if((entry->rec_len-ent_len)>=add_len)
		{
			new=(DIR_ENTRY*)((char*)entry+ent_len);
			new->rec_len=entry->rec_len-ent_len;		/* エントリーサイズの代入。 */
			new->name_len=add_len-sizeof(DIR_ENTRY);	/* ネームサイズの代入。 */
			entry->rec_len=ent_len;
			return new;
		}
	}

	return NULL;
}


/*
 * エントリーがあるかをチェックする。
 * parameters : device inode,directory block,directory parameters struct
 * return : 0 or エントリーあり=-1
 */
STATIC int checkEntry(int din,DIR_PARAM *dparam)
{
	DIR_ENTRY *entry;


	entry=dparam->dirBuf;

	if(entry->inode == 0)
	{
		if(entry->rec_len==superBlock[din]->blockSize)return 0;
	}
	else
	{
		if (cmpPath(entry->name,".",entry->name_len)==0)
		{
			entry=(DIR_ENTRY*)((char*)entry + entry->rec_len);
			if(entry->rec_len==superBlock[din]->blockSize-(sizeof(DIR_ENTRY)+sizeof(int)))return 0;
		}
	}

	return -1;
}


/*
* エントリーを削除する。
 * parameters : delete entry,directory buffer
 */
STATIC INLINE void delEntry(DIR_ENTRY *entry,DIR_ENTRY *dirBuf)
{
	DIR_ENTRY *p;


	if (entry == dirBuf)
		entry->inode = 0;
	else
	{
		for(p=dirBuf;(char*)p+p->rec_len != (char*)entry;p=(DIR_ENTRY*)((char*)p + p->rec_len));
		p->rec_len+=entry->rec_len;
	}
}


/*
 * 直接ブロック読込み。
 * 戻り値は与えられたバッファ内のディレクトリアドレス。
 * parameters : device inode,block table,number of block table indexs,directory parameters struct,node=0,READ_DIR struct
 * return : directory entry address or error number
 */
STATIC int readDirectDir(int din,uint *table,int blk_num,DIR_PARAM *dparam,int node,READ_DIR *readDirBlock)
{
	int sectors=superBlock[din]->sectors;
	DIR_ENTRY *entry;
	int i;


	for(i=0;i<blk_num;++i)
	{
		if(read_cache(din,dparam->dirBuf,sectors,blkToSect(din,table[i]))!=sectors)return -EIO;

		if((entry=searchEntry(din,dparam->path,dparam->dirBuf))!=NULL)
		{
			dparam->blkNumber=table[i];
			return (int)entry;
		}
	}

	return -ENOENT;
}


/*
 * 直接ブロック読込み。
 * 戻り値は与えられたバッファ内のディレクトリアドレス。
 * parameters : device inode,block table,number of block table indexs,directory parameters struct,node=0,READ_DIR struct
 * return : directory entry address or error number
 */
STATIC int searchDirectDir(int din,uint *table,int blk_num,DIR_PARAM *dparam,int node,READ_DIR *readDirBlock)
{
	int sectors=superBlock[din]->sectors;
	DIR_ENTRY *entry;
	int i;


	for(i=0;i<blk_num;++i)
	{
		if(read_cache(din,dparam->dirBuf,sectors,blkToSect(din,table[i]))!=sectors)return -EIO;
		if((entry=searchEntrySpace(din,dparam))!=NULL)
		{
			dparam->blkNumber=table[i];
			return (int)entry;
		}
	}

	return -ENOENT;
}


/*
 * 直接ブロック読込み。
 * parameters : device inode,block table,number of block table indexs,directory parameters struct,node=0,READ_DIR struct
 * return : なし=-ENOENT or error number
 */
STATIC int checkDirectDir(int din,uint *table,int blk_num,DIR_PARAM *dparam,int node,READ_DIR *readDirBlock)
{
	int sectors=superBlock[din]->sectors;
	int i;


	for(i=0;i<blk_num;++i)
	{
		if(read_cache(din,dparam->dirBuf,sectors,blkToSect(din,table[i]))!=sectors)return -EIO;
		if(checkEntry(din,dparam)==-1)return -EEXIST;
	}

	return -ENOENT;
}


/*
 * 次のディレクトリブロックを探す。
 * DIR_PARAMのblkNumberが0なら最初のブロック、0以上ならblkNumberの次のブロックを返す。
 * parameters : device inode,block table,number of block table indexs,directory parameters struct,node=0,READ_DIR struct
 * return : next block or error number
 */
STATIC int searchNextBlock(int din,uint *table,int blk_num,DIR_PARAM *dparam,int node,READ_DIR *readDirBlock)
{
	int i;


	if(dparam->blkNumber==0)return *table;
	else
		for(i=0;i<blk_num;++i)
			if(table[i]==dparam->blkNumber)
			{
				if(++i<blk_num)return table[i];
				dparam->blkNumber=0;
				break;
			}

	return -ENOENT;
}


/*
 * 間接読込み。
 * 戻り値は与えられたバッファ内のディレクトリアドレス。
 * parameters : device inode,block table,number of block table indexs,directory parameters struct,node,READ_DIR struct
 * return : directory entry address of error number
 */
STATIC int readIndirectDir(int din,uint *table,int blk_num,DIR_PARAM *dparam,int node,READ_DIR *readDirBlock)
{
	uint *buf;
	int sectors=superBlock[din]->sectors;
	int nblk=1<<((EXT2_BLOCK_BASE_LOG+superBlock[din]->s_log_block_size-INT_LOG)*node);
	int rest;
	int i;


	if((buf=(uint*)kmalloc(superBlock[din]->blockSize))==NULL)return -ENOMEM;

	for(i=0;blk_num>nblk;blk_num-=nblk,++i)
	{
		if((rest=read_cache(din,buf,sectors,blkToSect(din,table[i])))<0)goto END;
		if((rest=readDirBlock[node-1](din,buf,nblk,dparam,node-1,readDirBlock))!=-ENOENT)goto END;
	}
	if((rest=read_cache(din,buf,sectors,blkToSect(din,table[i])))<0)goto END;
	rest=readDirBlock[node-1](din,buf,blk_num,dparam,node-1,readDirBlock);

END:
	kfree(buf);

	return rest;
}


/*
 * read directory.
 * 戻り値は与えられたバッファ内のディレクトリアドレス。
 * parameters : path,device inode,inode,directory parameters struct,READ_DIR struct
 * return : directory address or error number
 */
STATIC int readDir(int din,INODE *inode,DIR_PARAM *dparam,READ_DIR *readDirBlock)
{
	uint *buf;
	int sectors=superBlock[din]->sectors;
	int block_size=superBlock[din]->blockSize;
	int blk_num=ROUNDUP_DIV(inode->i_size,block_size);
	int nblk;
	int rest;
	int blk,i;


	/* 直接ブロック。 */
	blk=(blk_num>=INODE_DATA_INDIRECT1)?INODE_DATA_INDIRECT1:blk_num;
	if((rest=readDirBlock[0](din,inode->i_block,blk,dparam,0,readDirBlock))!=-ENOENT)return rest;
	if((blk_num-=blk)==0)return -ENOENT;

	/* 間接ブロック。 */
	if((buf=(uint*)kmalloc(block_size))==NULL)return -ENOMEM;
	nblk=block_size/sizeof(uint);
	blk=nblk;
	for(i=INODE_DATA_INDIRECT1;;++i)
	{
		if((rest=read_cache(din,buf,sectors,blkToSect(din,inode->i_block[i])))<0)goto END;
		if((rest=readDirBlock[i-INODE_DATA_INDIRECT1](din,buf,(blk_num>blk)?blk:blk_num,dparam,i-INODE_DATA_INDIRECT1,readDirBlock))!=-ENOENT)
			goto END;
		if((blk_num-=blk)<=0)break;
		blk*=nblk;
	}
END:
	kfree(buf);

	return rest;
}


static READ_DIR readDirBlock[]={readDirectDir,readIndirectDir,readIndirectDir,readIndirectDir};
static READ_DIR addDirBlock[]={searchDirectDir,readIndirectDir,readIndirectDir,readIndirectDir};
static READ_DIR checkDirBlock[]={checkDirectDir,readIndirectDir,readIndirectDir,readIndirectDir};
static READ_DIR searchDirBlock[]={searchNextBlock,readIndirectDir,readIndirectDir,readIndirectDir};


/*
 * ディレクトリにエントリーが残っているか確認する。
 * parameters : device inode,directory inode
 * return : エントリーなし=-ENOENT or error number
 */
STATIC int INLINE checkDir(int din,INODE *inode)
{
	int rest;
	DIR_PARAM param;


	if((param.dirBuf=(DIR_ENTRY*)kmalloc(superBlock[din]->blockSize))==NULL)return -ENOMEM;
	rest=readDir(din,inode,&param,checkDirBlock);
	kfree(param.dirBuf);

	return rest;
}


/***************************** パブリックメソッド *****************************/


/*
 * inodeファイルタイプをディレクトリファイルタイプに変換する
 */
STATIC uchar getDirFileTypeFromFileType(const ushort type)
{
	switch (type & EXT2_S_IFMT)
	{
		case EXT2_S_IFSOCK:
			return EXT2_DIR_SOCK;
		case EXT2_S_IFLNK:
			return EXT2_DIR_SYMLINK;
		case EXT2_S_IFREG:
			return EXT2_DIR_REG_FILE;
		case EXT2_S_IFBLK:
			return EXT2_DIR_BLKDEV;
		case EXT2_S_IFDIR:
			return EXT2_DIR_DIR;
		case EXT2_S_IFCHR:
			return EXT2_DIR_CHRDEV;
		case EXT2_S_IFIFO:
			return EXT2_DIR_FIFO;
		default:
			return EXT2_DIR_UNKNOWN;
	}
}


/*
 * 親ディレクトリまで検索して、引数のparamに次のパスとディレクトリinodeを代入する。
 * parameters : device inode,INODE_PARAM struct,directory block buffer
 * return : 0 or error number
 */
STATIC int searchParent(int din, INODE_PARAM *param, DIR_ENTRY *dirBuf)
{
	int rest;
	DIR_ENTRY *entry;
	DIR_PARAM dparam;

	dparam.dirBuf = dirBuf;

	for (dparam.path = param->path; isLastPath(dparam.path) < 0;){
		/* ディレクトリエントリーを探す。 */
		if ((rest = readDir(din, param->inodeData, &dparam, readDirBlock)) < 0){
			return rest;
		}
		entry = (DIR_ENTRY*)rest;

		if (entry->file_type != EXT2_DIR_DIR){
			return -ENOTDIR;
		}

		/* inodeを得る。 */
		if ((rest = readInode(din, entry->inode, param->inodeBuf)) < 0){
			return rest;
		}
		param->inodeData = (INODE*)rest;
		param->inodeData->i_number = entry->inode;

		/* '/'の次にパスを進める。 */
		for (dparam.path += entry->name_len + 1; *dparam.path == '/'; ++dparam.path){;
		}
	}
	param->path = dparam.path;

	return 0;
}


// 親ディレクトリからパスのinodeを取得する。
// return : error number
STATIC int getInodeNumber(const int din, INODE_PARAM *param, DIR_ENTRY *dirBuf, uint *o_inode)
{
	int rest;
	DIR_ENTRY *entry;
	DIR_PARAM dparam;

	dparam.dirBuf = dirBuf;
	dparam.path = param->path;

	// ディレクトリエントリーを探す
	rest = readDir(din, param->inodeData, &dparam, readDirBlock);
	if (rest < 0){
		return rest;
	}
	entry = (DIR_ENTRY*)rest;

	*o_inode = entry->inode;

	return NOERR;
}


// return : error number
STATIC int getInodeData(const int din, const uint inodeNum, INODE_PARAM *param)
{
	int rest;

    // inodeデータを取得する。
    rest = readInode(din, inodeNum, param->inodeBuf);
	if (rest < 0){
		return rest;
	}
	param->inodeData = (INODE*)rest;
	param->inodeData->i_number = inodeNum;

	return NOERR;
}


/*
 * 親ディレクトリからパスのエントリーを得る。
 * parameters : device inode,INODE_PARAM struct,directory block buffer
 * return : 0 or error number
 */
STATIC int getInode(int din, INODE_PARAM *param, DIR_ENTRY *dirBuf) 
{
	int rest;
	DIR_ENTRY *entry;
	DIR_PARAM dparam;

	dparam.dirBuf = dirBuf;
	dparam.path = param->path;

	/* ディレクトリエントリーを探す。 */
	if ((rest = readDir(din, param->inodeData, &dparam, readDirBlock)) < 0){
		return rest;
	}
	entry = (DIR_ENTRY*)rest;

    /* inodeを得る。 */
	if ((rest = readInode(din, entry->inode, param->inodeBuf)) < 0){
		return rest;
	}
	param->inodeData = (INODE*)rest;
	param->inodeData->i_number = entry->inode;

	return 0;
}


/*
 * 親ディレクトリにエントリーを加える。
 * parameters : device inode,file type,inode number,INODE_PARAM struct,directory block buffer
 * return : 0 or error number
 */
STATIC int addDirEnt(int din,uchar type,uint inumber,INODE_PARAM *param,DIR_ENTRY *dirBuf)
{
	int block_size = superBlock[din]->blockSize;
	int sectors = superBlock[din]->sectors;
	DIR_ENTRY *new_ent;
	DIR_PARAM dparam;
	int rest;

	dparam.dirBuf = dirBuf;
	dparam.path = param->path;

	/* パスが存在するか確認。 */
	if (readDir(din,param->inodeData,&dparam,readDirBlock) > 0){
		return -EEXIST;
	}

	/* ディレクトリから空きエントリーを探す。 */
	rest = readDir(din,param->inodeData,&dparam,addDirBlock);
	if (rest == -ENOENT){
		/* 新規にディレクトリにブロックを割り当てる。 */
		if(superBlock[din]->s_free_blocks_count>0){
			new_ent=dparam.dirBuf;
			new_ent->rec_len=block_size;
			new_ent->name_len=strlen(dparam.path);
			memcpy(new_ent->name,param->path,new_ent->name_len);
			new_ent->inode = inumber;
			new_ent->file_type = type;

			rest = writeData(din,param->inodeData,new_ent,block_size,param->inodeData->i_size);
			if (0 < rest){
				param->inodeData->i_size += block_size;
				param->inodeData->i_blocks = ROUNDUP_DIV(param->inodeData->i_size,INODE_BLOCK_SIZE);
			}
			else{
				return rest;
			}
		}
		else{
			return -ENOSPC;
		}
	}
	else if (0 < rest){
		/* 新しいエントリーを初期化する。 */
		new_ent = (DIR_ENTRY*)rest;
		memcpy(new_ent->name,param->path,new_ent->name_len);
		new_ent->inode = inumber;
		new_ent->file_type = type;
		rest = write_cache(din,dparam.dirBuf,sectors,blkToSect(din,dparam.blkNumber));
		if (rest < 0){
			return rest;
		}
	}
	else{
		return rest;
	}

	/* 親ディレクトリinodeを更新する */
	param->inodeData->i_atime = param->inodeData->i_mtime = (uint)sys_time(NULL);
	if (type == EXT2_DIR_DIR){
		param->inodeData->i_links_count += 1;
	}
	rest = writeInode(din,param->inodeData->i_number,param->inodeBuf);
	if (rest < 0){
		return rest;
	}
	
	return 0;
}


/*
 * 親ディレクトリからファイルエントリーを削除する。
 * 削除したエントリーのinodeは引数のparamに代入する。
 * parameters : device inode,INODE_PARAM struct,directory block buffer
 * return 0 or error number
 */
STATIC int delFileEntry(int din, INODE_PARAM *param, DIR_ENTRY *dirBuf)
{
	int rest;
	DIR_ENTRY *entry;
	DIR_PARAM dparam;


	dparam.dirBuf = dirBuf;
	dparam.path = param->path;

	/* ディレクトリエントリーを探す。 */
	if ((rest = readDir(din,param->inodeData,&dparam,readDirBlock)) < 0)
		return rest;
	entry = (DIR_ENTRY*)rest;

	if (entry->file_type == EXT2_DIR_REG_FILE)
	{
		if ((rest = readInode(din,entry->inode,param->inodeBuf)) < 0)
			return rest;
		param->inodeData = (INODE*)rest;
		param->inodeData->i_number = entry->inode;
		delEntry(entry,dparam.dirBuf);
		if ((rest = write_cache(din,dparam.dirBuf,superBlock[din]->sectors,blkToSect(din,dparam.blkNumber))) < 0)
			return rest;
	}
	else
		return -ENOENT;

	return 0;
}


/*
 * 親ディレクトリからディレクトリエントリーを削除する。
 * 削除したエントリーのinodeは引数のparamに代入する。
 * parameters : name,device inode,INODE_PARAM struct,directory block buffer
 * return 0 or error number
 */
STATIC int delDirEntry(int din,INODE_PARAM *param,DIR_ENTRY *dirBuf)
{
	int rest;
	DIR_ENTRY *entry;
	DIR_PARAM dparam;
	INODE *parent_inode;
	INODE *parent_ibuf;


	dparam.dirBuf=dirBuf;
	dparam.path=param->path;

	/* ディレクトリエントリーを探す。 */
	if((rest=readDir(din,param->inodeData,&dparam,readDirBlock))<0)return rest;
	entry=(DIR_ENTRY*)rest;

	if (entry->file_type == EXT2_DIR_DIR)
	{
		parent_inode=param->inodeData;
		parent_ibuf=param->inodeBuf;

		if((param->inodeBuf=(INODE*)kmalloc(superBlock[din]->sectorSize))==NULL)return -ENOMEM;

		if((rest=readInode(din,entry->inode,param->inodeBuf))<0)goto ERR;
		param->inodeData=(INODE*)rest;
		param->inodeData->i_number=entry->inode;
		if((rest=checkDir(din,param->inodeData))!=-ENOENT)goto ERR;
		delEntry(entry,dparam.dirBuf);
		if((rest=write_cache(din,dparam.dirBuf,superBlock[din]->sectors,blkToSect(din,dparam.blkNumber)))<0)goto ERR;
		--parent_inode->i_links_count;
		if((rest=writeInode(din,parent_inode->i_number,parent_ibuf))<0)goto ERR;

		kfree(parent_ibuf);
	}
	else return -ENOENT;

	return 0;
ERR:
	kfree(parent_ibuf);

	return rest;
}


/*
 * エントリーのinodeをoldからnew入れ換える。
 * parameters : device inode,old inode parameter,new inode parameter,old block buffer,new block buffer
 *              old path,new path
 * return : 0 or error number
 */
STATIC int changeInode(int din,INODE_PARAM *old_param,INODE_PARAM *new_param,DIR_ENTRY *old_dbuf,DIR_ENTRY *new_dbuf,
	const char *old_path,const char *new_path)
{
	int rest;
	DIR_ENTRY *old_ent,*new_ent;
	DIR_PARAM old_dparam,new_dparam;


	/* oldのエントリーを探す。 */
	old_dparam.dirBuf=old_dbuf;
	old_dparam.path=old_param->path;
	if((rest=readDir(din,old_param->inodeData,&old_dparam,readDirBlock))<0)return rest;
	old_ent=(DIR_ENTRY*)rest;

	/* newのエントリーを探す。 */
	new_dparam.dirBuf=new_dbuf;
	new_dparam.path=new_param->path;
	rest=readDir(din,new_param->inodeData,&new_dparam,readDirBlock);

	if(rest>0)				/* newのエントリーが存在する。 */
	{
		new_ent=(DIR_ENTRY*)rest;
		if (old_ent->file_type != new_ent->file_type)
			return (old_ent->file_type == EXT2_DIR_REG_FILE)? -EISDIR : -ENOTDIR;

		/* path含有の確認。 */
		if (old_ent->file_type == EXT2_DIR_DIR)
			if(old_path[cmpString(old_path,new_path)]=='\0')
				return -EINVAL;

		/* newのデータとinodeを開放する。 */
		if ((rest = readInode(din,new_ent->inode,new_param->inodeBuf)) < 0)
			return rest;
		new_param->inodeData = (INODE*)rest;
		if (new_ent->file_type == EXT2_DIR_DIR)
			if ((rest = checkDir(din,new_param->inodeData)) != -ENOENT)
				return rest;
//		new_param->inodeData->i_number = new_ent->inode;
		rest = unlinkInode(din,new_param->inodeData);
		if (rest < 0)
			return rest;
		if (0 < rest)
			return 

		/* エントリーのinodeナンバーを入れ換える。 */
		new_ent->inode=old_ent->inode;
		if((rest=write_cache(din,new_dparam.dirBuf,superBlock[din]->sectors,blkToSect(din,new_dparam.blkNumber)))<0)
			return rest;
	}
	else if(rest==-ENOENT)	/* newのエントリーが存在しない。 */
	{
		/* ファイル名サイズの確認。 */
		if(strlen(new_param->path) > EXT2_NAME_LEN)
			return -ENAMETOOLONG;

		/* newエントリーを作成。 */
		if((rest=addDirEnt(din,old_ent->file_type,old_ent->inode,new_param,new_dbuf))<0)return rest;
	}
	else return rest;

	/* oldエントリーの削除。 */
	if((rest=readDir(din,old_param->inodeData,&old_dparam,readDirBlock))<0)return rest;	/* 再読み込み。 */
	delEntry(old_ent,old_dparam.dirBuf);
	if((rest=write_cache(din,old_dparam.dirBuf,superBlock[din]->sectors,blkToSect(din,old_dparam.blkNumber)))<0)return rest;

	return 0;
}


/*
 * 引数の名前の次のエントリーの名前を引数に代入して、次のエントリーのオフセットを返す。
 * 次のエントリーが無い場合は0を返す。
 * parameters : device inode,directory block,entry index,name
 * return : next index or 0 or error number
 */
STATIC int getNextEntry(int din, uint block, int index, char *o_name)
{
	int rest;
	int block_size = superBlock[din]->blockSize;
	DIR_ENTRY *buf,*entry;


	if (index == block_size){
		return 0;
	}

	buf = kmalloc(block_size);
	if (buf == NULL){
		return -ENOMEM;
	}
	rest = read_cache(din,buf,superBlock[din]->sectors,blkToSect(din,block));
	if (rest < 0){
		goto END;
	}

	entry = (DIR_ENTRY*)((char*)buf + index);
	if (entry->inode == 0){
		entry=(DIR_ENTRY*)((char*)entry+entry->rec_len);
	}

	if ((char*)entry == (char*)buf + block_size){
		rest = 0;
	}
	else{
		memcpy(o_name, entry->name, entry->name_len);
		o_name[entry->name_len] = '\0';
		rest = (uint)entry - (uint)buf + entry->rec_len;
	}
END:
	kfree(buf);

	return rest;
}


/*
 * 次のディレクトリブロックを取得する。次のブロックがない場合は-ENOENTを返す。
 * parameters : device inode,inode,directory block
 * retur : next block or error number
 */
STATIC int INLINE getNextBlock(int din,INODE *inode,uint block)
{
	DIR_PARAM dparam;


	dparam.blkNumber=block;
	return readDir(din,inode,&dparam,searchDirBlock);
}


/*
 * 新規ディレクトリ作成時のディレクトリブロックの初期化。
 * parameters : device inode,block number,inode number,parent inode number
 * return : 0 or error number
 */
STATIC int initDirBlock(int din,uint block,uint inumber,uint parent_inumber)
{
	int error=0;
	DIR_ENTRY *dent,*dent2;
	int rest;


	if((dent=(DIR_ENTRY*)kmalloc(superBlock[din]->sectorSize))==NULL)return -ENOMEM;
	memset(dent,0,superBlock[din]->sectorSize);			/* クリアーしないとディレクトリ操作で警告の元になる。 */

	/* "."ディレクトリの追加。 */
	dent->inode=inumber;
	dent->rec_len=sizeof(DIR_ENTRY)+sizeof(int);
	dent->name_len=1;
	dent->file_type = EXT2_DIR_DIR;
	dent->name[0]='.';

	dent2=(DIR_ENTRY*)((char*)dent+dent->rec_len);

	/* ".."ディレクトリの追加。 */
	dent2->inode=parent_inumber;
	dent2->rec_len=superBlock[din]->blockSize-dent->rec_len;
	dent2->name_len=2;
	dent2->file_type = EXT2_DIR_DIR;
	dent2->name[0]='.';
	dent2->name[1]='.';

	rest = write_cache(din,dent,1,blkToSect(din,block));
	if (rest < 0){
		error = rest;
	}

	kfree(dent);

	return error;
}


/***********************************************************************
 *
 * System call
 *
 ***********************************************************************/


/*
 * フィルを作成する
 * return : error number
 */
STATIC int creatFile(const char *path,int din,uint inumber,ushort mode,uint *o_number)
{
	uint parentInumber;
	uint block = 0;
	DIR_ENTRY *dirBuf;
	INODE *inode;
	INODE_PARAM param;
	PROC *proc;
	int rest,err_rest;

	/* パスの検索。 */
	if ((param.inodeBuf = kmalloc(superBlock[din]->sectorSize)) == NULL){
		return -ENOMEM;
	}
	if ((dirBuf = kmalloc(superBlock[din]->blockSize)) == NULL){
		kfree(param.inodeBuf);
		return -ENOMEM;
	}
	param.path = path;
	if ((rest = readInode(din,inumber,param.inodeBuf)) < 0){
		goto ERR1;
	}
	param.inodeData = (INODE*)rest;
	if ((rest = searchParent(din,&param,dirBuf)) < 0){
		goto ERR1;
	}
	parentInumber = param.inodeData->i_number;

	/* 空きinodeを取得する。 */
	if ((inumber = getNewInode(din,mode)) == 0){
		rest = -ENOSPC;
		goto ERR1;
	}

	/* ディレクトリエントリーを親ディレクトリに追加する。 */
	if ((rest = addDirEnt(din,getDirFileTypeFromFileType(mode),inumber,&param,dirBuf)) < 0){
		goto ERR2;
	}

	if ((mode & EXT2_S_IFMT) == EXT2_S_IFDIR){
		/* 新規ディレクトリを初期化する。 */
		block = getBlock(din);
		if (block == 0){
			rest = -ENOSPC;
			goto ERR3;
		}
		rest = initDirBlock(din,block,inumber,param.inodeData->i_number);
		if (rest < 0){
			goto ERR4;
		}
	}

	/* inodeを初期化する。 */
	if ((rest = readInode(din,inumber,param.inodeBuf)) < 0){
		goto ERR4;
	}
	inode = (INODE*)rest;
	memset(inode,0,sizeof(INODE));
	proc = get_current_task();
	inode->i_mode = mode;
	inode->i_uid = proc->uid;
	inode->i_gid = proc->gid;
	inode->i_atime = inode->i_ctime = inode->i_mtime = (uint)sys_time(NULL);
	inode->i_number = inumber;
	if ((mode & EXT2_S_IFMT) == EXT2_S_IFDIR){
		inode->i_links_count = 2;
		inode->i_size = superBlock[din]->blockSize;
		inode->i_blocks = inode->i_size/INODE_BLOCK_SIZE;
		inode->i_block[0] = block;
	}
	else{
		inode->i_links_count = 1;
	}
	if ((rest = writeInode(din,inumber,param.inodeBuf)) < 0){
		goto ERR4;
	}

	kfree(dirBuf);
	kfree(param.inodeBuf);
	
	*o_number = inumber;

	return NOERR;

ERR4:
	if ((mode & EXT2_S_IFMT) == EXT2_S_IFDIR)
		releaseBlock(din,block);
ERR3:
	if ((mode & EXT2_S_IFMT) == EXT2_S_IFDIR)
		if((err_rest = readInode(din,parentInumber,param.inodeBuf)) > 0)
		{
			((INODE*)err_rest)->i_links_count -= 1;
			writeInode(din,parentInumber,param.inodeBuf);
		}
ERR2:
	releaseInodeBitAndGrp(din,inumber,EXT2_S_IFLNK);
ERR1:
	kfree(dirBuf);
	kfree(param.inodeBuf);

	return rest;
}


STATIC int mount(const int din, void **o_rootInodeObj)
{
	void *buf;
	DEV_STAT devStat;
	INODE *inode, *inodeBuf;
	int sectors;
	int gd;
	int rest;

	// すでにマウントされているか確認。
	if (superBlock[din] != NULL){
		return -EBUSY;
	}

	// スーパーブロックを読み込む。
	rest = get_devstat(din, &devStat);
	if (rest < 0){
		return rest;
	}
	buf = kmalloc(PAGE_SIZE);
	if (buf == NULL){
		return -ENOMEM;
	}
	rest = read_direct(din, buf, PAGE_SIZE / devStat.sect_size, 0);
	if (rest != PAGE_SIZE / devStat.sect_size){
		goto ERR;
	}
	superBlock[din] = kmalloc(sizeof(SUPER_BLOCK));
	if (superBlock[din] == NULL){
		rest = -ENOMEM;
		goto ERR;
	}
	memcpy(superBlock[din], (char*)buf + EXT2_SUPER_BLK_POSITION, sizeof(SUPER_BLOCK));

	/* マジックナンバーの確認。 */
	if (superBlock[din]->s_magic != EXT2_SUPER_MAGIC){
		rest = -ENOTBLK;
		goto ERR;
	}

	/* 独自スーパーブロック要素定義。 */
	superBlock[din]->blockSize = EXT2_BLOCK_BASE << superBlock[din]->s_log_block_size;
	superBlock[din]->sectorSize = devStat.sect_size;
	superBlock[din]->sectors = superBlock[din]->blockSize / superBlock[din]->sectorSize;

	sectors = superBlock[din]->sectors;

	// グループディスクリプターを読み込む。
	grpDescNum[din] = ROUNDUP_DIV(superBlock[din]->s_blocks_count, superBlock[din]->s_blocks_per_group);
	grpDescBlocks[din] = ROUNDUP_DIV(grpDescNum[din] * sizeof(GROUP_DESC), superBlock[din]->blockSize);
	grpDesc[din] = kmalloc(superBlock[din]->blockSize * grpDescBlocks[din]);
	if (grpDesc[din] == NULL){
		rest = -ENOMEM;
		goto ERR2;
	}
	rest = read_direct(din,grpDesc[din],sectors * grpDescBlocks[din],
		blkToSect(din,EXT2_GROUP_DESC_BLK + superBlock[din]->s_first_data_block));
	if (rest != sectors * grpDescBlocks[din]){
		goto ERR3;
	}

	/* ブロック関係の初期化。 */
	rest = initBlock(din);
	if (rest < 0){
		goto ERR3;
	}
	gd = rest;

	/* inode関係の初期化。 */
	rest = initInode(din, gd);
	if (rest < 0){
		goto ERR3;
	}

	/* root inodeを読み出す */
	rest = readInode(din, EXT2_ROOT_INODE_INDEX, buf);
	if (rest < 0){
		goto ERR3;
	}
	inode = (INODE*)rest;
	inodeBuf = kmalloc(sizeof(INODE));
	if (rest < 0){
		rest = -ENOMEM;
		goto ERR3;
	}
	memcpy(inodeBuf, inode, sizeof(INODE));
	*o_rootInodeObj = inodeBuf;
	
	kfree(buf);

	return 0;

ERR3:
	kfree(grpDesc[din]);
	grpDesc[din] = NULL;
ERR2:
	kfree(superBlock[din]);
	superBlock[din] = NULL;
ERR:
	kfree(buf);

	return rest;
}


STATIC int umount(const int din, void *rootInodeObj)
{
	umountInode(din);
	umountBlock(din);
	writeBackSbGd(din);

	kfree(rootInodeObj);

	kfree(grpDesc[din]);
	grpDesc[din] = NULL;

	kfree(superBlock[din]);
	superBlock[din] = NULL;

	return NOERR;
}


//《注意》NULLパスは不可。
STATIC int lookup(const char *path, const int din, void *inodeObj, uint *o_inodeNum)
{
	int rest;
	INODE *dirInode = inodeObj;
	DIR_ENTRY *dirBuf;
	INODE_PARAM param;

	if ((param.inodeBuf = kmalloc(superBlock[din]->sectorSize)) == NULL){
		return -ENOMEM;
	}
	if ((dirBuf = kmalloc(superBlock[din]->blockSize)) == NULL){
		kfree(param.inodeBuf);
		return -ENOMEM;
	}
	param.path = path;
	if ((rest = readInode(din, dirInode->i_number, param.inodeBuf)) < 0){
		goto END;
	}
	param.inodeData = (INODE*)rest;
	if ((rest = searchParent(din, &param, dirBuf)) < 0){
		goto END;
	}
	rest = getInodeNumber(din, &param, dirBuf, o_inodeNum);
	if (rest < 0){
		goto END;
	}

	rest = NOERR;
END:
	kfree(dirBuf);
	kfree(param.inodeBuf);

	return rest;
}


//《注意》NULLパスは不可。
STATIC int creat(const char *path, const int din, void *inodeObj, const int mode, uint *o_inodeNum)
{
	INODE *dirInode = inodeObj;

	// ファイルを作成する
	return creatFile(path, din, dirInode->i_number, mode, o_inodeNum);
}


STATIC int open(const int din, const uint inodeNum, int oflag, void **o_inodeObj, int *o_type)
{
	INODE *inodeData;
	INODE_PARAM inodeParam;
	int rest;

	inodeParam.inodeBuf = kmalloc(superBlock[din]->sectorSize);
	if (inodeParam.inodeBuf == NULL){
		return -ENOMEM;
	}
	rest = getInodeData(din, inodeNum, &inodeParam);
	if (rest < 0){
		goto END;
	}

	inodeData = kmalloc(sizeof(INODE));
	if (inodeData == NULL){
		rest = -ENOMEM;
		goto END;
	}
	memcpy(inodeData, inodeParam.inodeData, sizeof(INODE));

	*o_inodeObj = inodeData;
	*o_type = inodeData->i_mode & EXT2_S_IFMT;
	
	rest = NOERR;
END:
	kfree(inodeParam.inodeBuf);

	return rest;
}


STATIC int close(const int din, void *inodeObj)
{
	INODE *sectBuf;
	INODE *diskInode;
	INODE *inode = inodeObj;
	int rest;

	// inodeオブジェクトを上書きして開放する
	sectBuf = kmalloc(superBlock[din]->sectorSize);
	if (sectBuf == NULL){
		return -ENOMEM;
	}
	rest = readInode(din, inode->i_number, sectBuf);
	if (rest < 0){
		goto END;
	}
	diskInode = (INODE*)rest;
	if (diskInode->i_dtime == 0){
		memcpy(diskInode, inode, sizeof(INODE));
		rest = writeInode(din, inode->i_number, sectBuf);
		if (rest < 0){
			goto END;
		}
	}

	rest = NOERR;
END:
	kfree(sectBuf);
	kfree(inode);

	return rest;
}


STATIC int read(const int din, void *inodeObj, void *buf, size_t size, size_t begin)
{
	INODE *inode = inodeObj;
	int rest;

	/* サイズの確認。 */
	if (inode->i_size <= begin){
		return 0;
	}
	if (inode->i_size <= begin + size){
		size = inode->i_size - begin;
	}

	/* ファイルから読み込む。 */
	rest = readData(din, inode, buf, size, begin);
	if (rest < 0){
		return rest;
	}

	/* 日付の更新。 */
	inode->i_atime = (uint)sys_time(NULL);

	return size;
}


STATIC int write(const int din, void *inodeObj, void *buf, size_t size, size_t begin)
{
	INODE *inode = inodeObj;
	int rest;

	/* サイズの確認。 */
	if (inode->i_size < begin + size){
		if(superBlock[din]->s_free_blocks_count <= (begin + size - inode->i_size) / superBlock[din]->blockSize){
			return -ENOSPC;
		}
	}

	/* ファイルへ書き込み。 */
	if ((rest = writeData(din, inode, buf, size, begin)) < 0){
		return rest;
	}

	/* サイズの更新。 */
	if (inode->i_size < begin + size){
		int ext2BlkSize = superBlock[din]->blockSize;
		int inodeNum = ext2BlkSize / sizeof(uint32_t);
		int blkNum = ROUNDUP_DIV(begin + size, ext2BlkSize);

		inode->i_size = begin + size;

		if (INODE_DATA_INDIRECT1 < blkNum){
			if (INODE_DATA_INDIRECT1 + inodeNum < blkNum){
				if (INODE_DATA_INDIRECT1 + (inodeNum + 1) * inodeNum < blkNum){
					blkNum += ROUNDUP_DIV(blkNum - INODE_DATA_INDIRECT1 - inodeNum, inodeNum * inodeNum);
				}
				blkNum += ROUNDUP_DIV(blkNum - INODE_DATA_INDIRECT1, inodeNum);
			}
			blkNum += 1;
		}
		inode->i_blocks = blkNum * (ext2BlkSize / INODE_BLOCK_SIZE);
	}

	/* 日付の更新。 */
	inode->i_atime = inode->i_mtime = sys_time(NULL);

	return size;
}


//《注意》NULLパスは不可。
STATIC int mkdir(const char *path, const int din, void *dirInodeObj, int mode)
{
	INODE *dirInode = dirInodeObj;
	uint inumber;

	return creatFile(path, din, dirInode->i_number, mode | S_IFDIR, &inumber);
}


//《注意》NULLパスは不可。
STATIC int delete(const char *path, const int din, void *dirInodeObj, int type)
{
	INODE *dirInode = dirInodeObj;
	DIR_ENTRY *dirBuf;
	INODE_PARAM param;
	int rest;

	/* 親ディレクトリの検索。 */
	param.inodeBuf = kmalloc(superBlock[din]->sectorSize);
	if (param.inodeBuf == NULL){
		return -ENOMEM;
	}
	dirBuf = kmalloc(superBlock[din]->blockSize);
	if (dirBuf == NULL){
		kfree(param.inodeBuf);
		return -ENOMEM;
	}
	param.path = path;
	rest = readInode(din, dirInode->i_number, param.inodeBuf);
	if (rest < 0){
		goto END;
	}
	param.inodeData = (INODE*)rest;
	rest = searchParent(din,&param, dirBuf);
	if (rest < 0){
		goto END;
	}

	/* 親ディレクトリからディレクトリエントリーを削除する。 */
	switch (type){
		case S_IFREG:
			rest = delFileEntry(din, &param, dirBuf);
			if (rest < 0){
				goto END;
			}
			break;
		case S_IFDIR:
			rest = delDirEntry(din, &param, dirBuf);
			if (rest < 0){
				goto END;
			}
			break;
		default:
			rest = -EOPNOTSUPP;
			goto END;
	}

	/* inodeを開放する */
	rest = unlinkInode(din, param.inodeData);
	if (rest < 0){
		goto END;
	}

	rest = NOERR;
END:
	kfree(dirBuf);
	kfree(param.inodeBuf);

	return rest;
}


STATIC int opendir(const char *path, const int din, void *dirInodeObj, void **o_inodeObj, uint *o_block, int *o_index)
{
	INODE *dirInode = dirInodeObj;
	INODE *inodeBuf;
	DIR_ENTRY *dirBuf;
	INODE_PARAM param;
	int rest;

	/* 各バッファのアロケート */
	param.inodeBuf = kmalloc(superBlock[din]->sectorSize);
	if (param.inodeBuf == NULL){
		return -ENOMEM;
	}
	dirBuf = kmalloc(superBlock[din]->blockSize);
	if (dirBuf == NULL){
		rest = -ENOMEM;
		goto END;
	}

	/* inodeを検索する */
	rest = readInode(din, dirInode->i_number, param.inodeBuf);
	if (rest < 0){
		goto END2;
	}
	if (*path == '\0')
	{
		*o_block = ((INODE*)rest)->i_block[0];
		*o_inodeObj = dirInode;
	}
	else
	{
		param.path = path;
		param.inodeData = (INODE*)rest;
		rest = searchParent(din,&param, dirBuf);
		if (rest < 0){
			goto END2;
		}
		rest = getInode(din,&param,dirBuf);
		if (rest < 0){
			goto END2;
		}
		if ((param.inodeData->i_mode & EXT2_S_IFMT) != EXT2_S_IFDIR){
			rest = -ENOTDIR;
			goto END2;
		}
		
		/* inodeを作成する */
		inodeBuf = kmalloc(sizeof(INODE));
		if (inodeBuf == NULL){
			rest = -ENOMEM;
			goto END2;
		}
		memcpy(inodeBuf, param.inodeData, sizeof(INODE));

		*o_block = param.inodeData->i_block[0];
		*o_inodeObj = inodeBuf;
	}
	*o_index = 0;
	rest = NOERR;
END2:
	kfree(dirBuf);
END:
	kfree(param.inodeBuf);

	return rest;
}


STATIC int readdir(int din, void *inodeObj, uint *m_block, int *m_index, char *o_name)
{
	INODE *dirInode = inodeObj;
	INODE *inode;
	INODE_PARAM param;
	int rest;

	for(;;){
		rest = getNextEntry(din, *m_block, *m_index, o_name);
		if(0 < rest){
			*m_index = rest;
			rest = 0;
			break;
		}
		else if(rest < 0){
			break;
		}

		if((param.inodeBuf = kmalloc(superBlock[din]->sectorSize)) == NULL){
			return -ENOMEM;
		}
		if((rest = readInode(din, dirInode->i_number, param.inodeBuf)) < 0){
			kfree(param.inodeBuf);
			break;
		}
		inode = (INODE*)rest;
		rest = getNextBlock(din, inode, *m_block);
		kfree(param.inodeBuf);
		if(rest == -ENOENT){		/* 次のブロック無し。 */
			*o_name = '\0';
			rest = 0;
			break;
		}
		else if(rest < 0){
			break;
		}

		*m_block = rest;
		*m_index = 0;
	}

	return rest;
}


STATIC int stat(int din, void *inodeObj, struct stat *m_stat)
{
	INODE *inode = inodeObj;

	m_stat->st_dev		= din;
	m_stat->st_mode		= inode->i_mode;
	m_stat->st_nlink	= inode->i_links_count;
	m_stat->st_uid		= inode->i_uid;
	m_stat->st_gid		= inode->i_gid;
	m_stat->st_size		= inode->i_size;
	m_stat->st_atime	= inode->i_atime;
	m_stat->st_mtime	= inode->i_mtime;
	m_stat->st_ctime	= inode->i_ctime;
	m_stat->st_blksize	= EXT2_BLOCK_BASE << superBlock[din]->s_log_block_size;
	m_stat->st_ino		= inode->i_number;										/* とりあえずinodeを入れる。 */
	m_stat->st_blocks	= ROUNDUP(m_stat->st_size,m_stat->st_blksize) / 512;	/* 使用しているブロック数(512バイト単位)。 */
	m_stat->st_rdev		= 0;

	return 0;
}


STATIC int rename(int din, void *oldDirInodeObj, const char *oldPath, void *newDirInodeObj, const char *newPath)
{
	INODE *oldDirInode = oldDirInodeObj;
	INODE *newDirInode = newDirInodeObj;
	DIR_ENTRY *dirBuf1, *dirBuf2;
	INODE_PARAM old_param, new_param;
	int rest;

	new_param.inodeBuf = NULL;
	dirBuf2 = NULL;

	/* old pathを探す。 */
	dirBuf1 = kmalloc(superBlock[din]->blockSize);
	if (dirBuf1 == NULL){
		return -ENOMEM;
	}
	old_param.inodeBuf = kmalloc(superBlock[din]->sectorSize);
	if (old_param.inodeBuf == NULL){
		rest = -ENOMEM;
		goto END;
	}
	old_param.path = oldPath;
	rest = readInode(din, oldDirInode->i_number, old_param.inodeBuf);
	if (rest < 0){
		goto END2;
	}
	old_param.inodeData = (INODE*)rest;
	rest=searchParent(din, &old_param, dirBuf1);
	if (rest < 0){
		goto END2;
	}

	/* new pathを探す。 */
	new_param.inodeBuf = kmalloc(superBlock[din]->sectorSize);
	if (new_param.inodeBuf == NULL){
		rest = -ENOMEM;
		goto END2;
	}
	new_param.path = newPath;
	rest = readInode(din,newDirInode->i_number, new_param.inodeBuf);
	if (rest < 0){
		goto END3;
	}
	new_param.inodeData = (INODE*)rest;
	rest = searchParent(din,&new_param, dirBuf1);
	if (rest < 0){
		goto END3;
	}

	/* old inodeとnew inodeを入れ換える。 */
	dirBuf2 = kmalloc(superBlock[din]->blockSize);
	if (dirBuf2 == NULL){
		rest = -ENOMEM;
		goto END3;
	}
	rest = changeInode(din, &old_param, &new_param, dirBuf1, dirBuf2, oldPath, newPath);
	if (rest < 0){
		goto END4;
	}

	rest = NOERR;
END4:
	kfree(dirBuf2);
END3:
	kfree(new_param.inodeBuf);
END2:
	kfree(old_param.inodeBuf);
END:
	kfree(dirBuf1);

	return rest;
}


STATIC int chattr(int din, void *inodeObj, uint mode, uint uid, uint gid, uint atime, uint mtime)
{
	INODE *inode = inodeObj;

	inode->i_mode 	= (inode->i_mode & ~0x1ff) | (mode & 0x1ff);
	inode->i_uid 	= uid;
	inode->i_gid 	= gid;
	inode->i_atime 	= atime;
	inode->i_mtime 	= mtime;

	return NOERR;
}


STATIC int link(const int din, void *dstInodeObj, void *srcInodeObj, const char *srcPath)
{
	INODE *dstInode = dstInodeObj;
	INODE *srcInode = srcInodeObj;
	DIR_ENTRY *dirBuf;
	INODE_PARAM param;
	int rest;

	if ((param.inodeBuf = kmalloc(superBlock[din]->sectorSize)) == NULL){
		return -ENOMEM;
	}
	if ((dirBuf = kmalloc(superBlock[din]->blockSize)) == NULL){
		kfree(param.inodeBuf);
		return -ENOMEM;
	}

	/* リンク先のディレクトリinode領域を探す */
	param.path = srcPath;
	if ((rest = readInode(din,srcInode->i_number,param.inodeBuf)) < 0){
		goto END;
	}
	param.inodeData = (INODE*)rest;
	if ((rest = searchParent(din,&param,dirBuf)) < 0){
		goto END;
	}

	/* リンク先のファイル名サイズのチェック。 */
	if (strlen(param.path) > EXT2_NAME_LEN){
		rest = -ENAMETOOLONG;
		goto END;
	}

	/* ディレクトリエントリーを追加する。 */
	if ((rest = addDirEnt(din, getDirFileTypeFromFileType(EXT2_S_IFREG), dstInode->i_number, &param, dirBuf)) < 0){
		goto END;
	}

	/*
	 * 元のinodeは書き戻さずに置く
	 */
//	if ((rest = readInode(din,dstInode->i_number,param.inodeBuf)) < 0)
//		goto END;
	/* リンクを増やす */
	dstInode->i_links_count += 1;
//	memcpy((void*)rest,(void*)dstInode,sizeof(INODE));
//	if ((rest = writeInode(din,dstInode->i_number,param.inodeBuf)) < 0)
//		goto END;
	
	rest = NOERR;
END:
	kfree(dirBuf);
	kfree(param.inodeBuf);

	return rest;
}


STATIC int symlink(const char *filePath, const int din, void *dirInodeObj, char *linkPath)
{
	uint inumber;
	INODE *inode;
	INODE *sectBuf;
	INODE *dirInode = dirInodeObj;
	int rest;

	rest = creatFile(filePath, din, dirInode->i_number, S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO, &inumber);
	if (rest < 0){
		return rest;
	}

	/* inodeを読み出す */
	sectBuf = kmalloc(superBlock[din]->sectorSize);
	if (sectBuf == NULL){
		return -ENOMEM;
	}
	rest = readInode(din, inumber, sectBuf);
	if (rest < 0){
		goto END;
	}
	inode = (INODE*)rest;

	/* リンクパスを書き込む */
	rest = write(din, inode, linkPath, strlen(linkPath), 0);
	if (rest < 0){
		goto END;
	}
	
	/* inodeを書き戻す */
	rest = writeInode(din, inumber, sectBuf);
	if (rest < 0){
		goto END;
	}

	rest = NOERR;
END:
	kfree(sectBuf);

	return rest;
}


STATIC int ioctl(int din, void *inodeObj, int cmd, caddr_t param, int fflag)
{
	int rest;

	switch(cmd){
		case I_TRUNC:
			/* truncate. */
			rest = releaseDataBlock(din, inodeObj);
			if(rest < 0){
				return rest;
			}
	}

	return NOERR;
}


STATIC int poll(void *inodeObj, int events)
{
	return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
}


STATIC int statfs(const int din, struct statfs *m_statfs)
{
	SUPER_BLOCK *superBlk = superBlock[din];

	m_statfs->f_bsize		= superBlk->blockSize;				/* File system block size. */
	m_statfs->f_iosize		= superBlk->blockSize;				/* optimal transfer block size */
	m_statfs->f_blocks		= superBlk->s_blocks_count;			/* total data blocks in file system */
	m_statfs->f_bfree		= superBlk->s_free_blocks_count;	/* free blocks in fs */
	m_statfs->f_bavail		= superBlk->s_free_blocks_count;	/* free blocks avail to non-superuser */
	m_statfs->f_files		= superBlk->s_inodes_count;			/* total file nodes in file system */
	m_statfs->f_ffree		= superBlk->s_free_inodes_count;	/* free file nodes in fs */
	m_statfs->f_fsid.val[0] = 0;								/* file system id */
	m_statfs->f_fsid.val[1] = 0;
	m_statfs->f_owner		= 0;								/* user that mounted the filesystem */
	m_statfs->f_type		= MOUNT_EXT2FS;						/* type of filesystem */
	m_statfs->f_flags		= 0;								/* Bit mask of f_flag values */
	memcpy(m_statfs->f_mntfromname, "ext2fs", 7);					/* mounted filesystem */

	return NOERR;
}


/***********************************************************************
 *
 * Init
 *
 ***********************************************************************/

static FS ext2Fs={
	"ext2",
	mount,
	umount,
	lookup,
	creat,
	open,
	close,
	read,
	write,
	ioctl,
	poll,
	opendir,
	readdir,
	rename,
	mkdir,
	stat,
	delete,
	chattr,
	link,
	symlink,
	statfs,
};


/*
 * GLOBAL
 */
int initExt2tFs()
{
	int i;


	for(i=0;i<MAX_DEVICE_OPEN;++i)
	{
		grpDesc[i]=NULL;
		superBlock[i]=NULL;
	}

	return regist_fs(&ext2Fs);
}
/**************************************************************************************/
int test_ext2(int din)
{
	printk("test_ext2(() currentGd=0x%x\n", blockBm[din].currentGd);

	return 0;
}
/**************************************************************************************/
