/*
 * orig_fs.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * オリジナルファイルシステム
 */


#include"config.h"
#include"types.h"
#include"lib.h"
#include"errno.h"
#include"mm.h"
#include"fs.h"
#include"device.h"
#include"orig_fs.h"


enum{
	MAX_NAME_SIZE=127,	/* ファイル名サイズ、可変 */
	MAX_DIR_NUM=4,		/* 最大ディレクトリブロック数、可変 */
};


/* Inode */
typedef struct{
	ushort owner_id;			/* Owner UID */
	ushort group_id;			/* Group ID */
	size_t size;				/* File size by byte */
	uint64 access_time;			/* Last access time */
	uint64 create_time;			/* Create time */
	uint64 modificat_time;		/* Last modification time */
	uint my_secter;				/* my block number */
	ushort file_type;			/* File type */
	ushort indirect;			/* 間接参照ノード数 */
	uint secter[0];				/* file secter number */
}INODE;

/* Directory entry
 * 関数del_entry()が要素の配置に依存している。
 */
typedef struct DIR_ENT{
	uint secter;		/* secter number */
	uchar type;			/* File or directory */
	uchar size;			/* entry size */
	uchar low_dir;		/* low entry directory */
	uchar high_dir;		/* high entry directory */
	ushort low;			/* low entry offset */
	ushort high;		/* high entry offset */
	ushort low_num;		/* lowエントリー数 */
	ushort high_num;	/* highエントリー数 */
	char name[0];		/* Name string */
}DIR_ENT;

/* Directory empty entry */
typedef struct{
	ushort prev;	/* prev empty entry */
	ushort next;	/* next empty entry */
	int size;		/* entry size */
}EMPTY_ENT;

/* Directory top */
typedef struct{
	uint my_secter;			/* my block number */
	uint next_dir;			/* next directory link */
	ushort empty_prev;		/* empty entry offset */
	ushort empty_next;		/* empty entry offset */
	uchar entry_dir;		/* Top entry direcctory */
	ushort entry_top;		/* Top entry offset */
	ushort size;			/* directory size */
	ushort owner_id;		/* Owner UID */
	ushort group_id;		/* Group ID */
	uint64 access_time;		/* Last access time */
	uint64 create_time;		/* Create time */
	uint64 modificat_time;	/* Last modification time */
	DIR_ENT entry[0];		/* begin entry */
}DIR;


#define ENTRY(entry,buf) ((DIR_ENT*)(entry+(uint)buf))
#define EMPTY(empty,buf) ((EMPTY_ENT*)(empty+(uint)buf))


static SUPER_BLOCK *super_blk[MAX_DEVICE_OPEN];
static ENPTY_BLOCK *empty_blk[MAX_DEVICE_OPEN];
static int indirect_size[3]={
	BLOCK_SIZE,
	BLOCK_SIZE*(BLOCK_SIZE/sizeof(uint)),
	BLOCK_SIZE*(BLOCK_SIZE/sizeof(uint))*(BLOCK_SIZE/sizeof(uint))
};


static uint mount(int);
static int umount(int);
static int open(const char*,int,uint);
static int close(int,uint);
static int read(int,uint,void*,size_t,size_t);
static int write(int,uint,void*,size_t,size_t);
static int ioctr(int,uint,int,void*);
static int rename(int,uint,const char*);
static int creat(const char*,int,uint);
static uint opendir(const char*,int,uint);
static int mkdir(const char*,int,uint);


static FS orig_fs={
	"orig_fs",mount,umount,open,close,read,write,ioctr,rename,creat,opendir,mkdir
};


/*
 * cmpare path strings
 * parameters : Destination string(end='\0'),Sorce string(end='/' or '\0')
 * return : 一致=次のパスポインタ or 大=1 or 小=-1;
 */
extern inline int cmp_path(const char *path1,const char *path2)
{
	while(*path1==*path2++)
		if(*path1++=='\0')return (int)path2-1;

	if((*(path2-1)=='/')&&(*path1=='\0'))return (int)path2;
	if(*path1<*(path2-1))return 1;

	return -1;
}


/*
 * 最後のパス文字列かどうかを判定する。
 * parameters : path
 * return : 最後 1,最後でない 0
 */
extern inline int is_last_path(const char *path)
{
	if(*path=='\0')return 1;

	while(*path++!='/')
		if(*path=='\0')return 1;

	return 0;
}


/************************************************************************************
 *
 * Init functions
 *
 ************************************************************************************/

int init_orig_fs()
{
	int i;


	for(i=0;i<MAX_DEVICE_OPEN;++i)
	{
		super_blk[i]=NULL;
		empty_blk[i]=NULL;
	}

	return regist_fs(&orig_fs);
}


/************************************************************************************
 *
 * block  functions
 *
 ************************************************************************************/

/*
 * 空きブロックを取得する。
 * parameters : device inode
 * return : empty block secter or error=0
 */
uint get_empty_blk(int din)
{
	uint secter;


	if((secter=empty_blk[din]->empty[empty_blk[din]->current++])==0)return 0;

	if(empty_blk[din]->current>(BLOCK_SIZE-sizeof(ENPTY_BLOCK))/sizeof(uint))
		if(read_cache(din,empty_blk[din],super_blk[din]->secters,secter)!=super_blk[din]->secters)
			return 0;

	return secter;
}


/*
 * ブロックを開放して、空きブロックインデックスに加える。
 * parameters :
 * return : 0 or error=-1
 */
int release_blk(int din,uint blk)
{
	if(--empty_blk[din]->current<0)
	{
		if((write_direct(din,empty_blk[din],super_blk[din]->secters,blk))!=super_blk[din]->secters)
			return -1;
		empty_blk[din]->current=(BLOCK_SIZE-sizeof(ENPTY_BLOCK))/sizeof(uint);
	}

	empty_blk[din]->empty[empty_blk[din]->current]=blk;

	return 0;
}


/***********************************************************************************
 *
 * directory functions
 * エントリーを二分木構造にする。
 *
 ************************************************************************************/

/*
 * エントリーをlow側に回転させる。
 * parameters : directory buffer number pointer,entry offset pointer,directory buffer
 * return : new top entry
 */
void round_low(uchar *dn,ushort *entry,DIR **dir)
{
	uchar a;
	ushort b,c;
	DIR_ENT *top,*high;


	top=ENTRY(*entry,dir[*dn]);
	if((high=ENTRY(top->high,dir[top->high_dir]))==(DIR_ENT*)dir[0])return;

	/* topエントリーを保存。 */
	a=*dn;
	b=*entry;
	c=top->low_num+top->high_num;

	/* highエントリーをtopの親エントリーの子ノードに移動。 */
	*entry=top->high;
	*dn=top->high_dir;

	/* highのlowエントリーをtopのhighエントリーに移動。 */
	top->high_dir=high->low_dir;
	top->high=high->low;
	top->high_num=high->low_num;

	/* topをhighのlowエントリーに移動。 */
	high->low_dir=a;
	high->low=b;
	high->low_num=c;
}


/*
 * エントリーをhigh側に回転させる。
 * parameters : directory buffer number pointer,entry offset pointer,directory buffer
 * return : new top entry
 */
void round_high(uchar *dn,ushort *entry,DIR **dir)
{
	uchar a;
	ushort b,c;
	DIR_ENT *top,*low;


	top=ENTRY(*entry,dir[*dn]);
	if((low=ENTRY(top->low,dir[top->low_dir]))==(DIR_ENT*)dir[0])return;

	/* topエントリーを保存。 */
	a=*dn;
	b=*entry;
	c=top->low_num+top->high_num;

	/* highエントリーをtopの親エントリーの子ノードに移動。 */
	*entry=top->low;
	*dn=top->low_dir;

	/* highのlowエントリーをtopのhighエントリーに移動。 */
	top->low_dir=low->high_dir;
	top->low=low->high;
	top->low_num=low->high_num;

	/* topをhighのlowエントリーに移動。 */
	low->high_dir=a;
	low->high=b;
	low->high_num=c;
}


/*
 * 二分木扁平化関数
 * 片側のエントリー数が反対側のエントリー数の2倍より多く、かつ
 * その片側の内側のエントリー数が片側のエントリー数の1/2より少ない場合、反対側へ回転させる。
 * parameters : directory buffer number pointer,entry offset pointer,directory buffer
 */
void splay(uchar *dn,ushort *entry,DIR **dir)
{
	DIR_ENT *top;


	top=ENTRY(*entry,dir[*dn]);
	if((top->high_num>top->low_num*2)&&(ENTRY(top->high,dir[top->high_dir])->low_num<top->high_num/2))
		round_low(dn,entry,dir);
	else if((top->low_num>top->high_num*2)&&(ENTRY(top->low,dir[top->low_dir])->high_num<top->low_num/2))
		round_high(dn,entry,dir);
}


/*
 * ディレクトリ内の同じ名前のエントリーを探す。
 * 検索がヒットした場合、引数のnameに次のパスの先頭ポインタを返す。
 * parameters : name address pointer,directory buffer
 * return : directory entry or error=NULL
 */
DIR_ENT *search_same_entry(const char **name,DIR **dir)
{
	int r;
	DIR_ENT *p;


	for(p=ENTRY(dir[0]->entry_top,dir[dir[0]->entry_dir]);p!=(DIR_ENT*)dir[0];)
	{
		r=cmp_path(p->name,*name);
		if(r==-1)p=ENTRY(p->low,dir[p->low_dir]);
		else if(r==1)p=ENTRY(p->high,dir[p->high_dir]);
		else
		{
			*name=(const char*)r;
			return p;
		}
	}

	return NULL;
}


/*
 * ディレクトリ内で引数の名前の親エントリーを取得する。
 * ディレクトリ内の最初のエントリーが存在することを前提とする。
 * parameters : name,directory buffer pointer,entry directory buffer pointer,entry offset pointer
 * return : 0 or error=-1
 */
int search_prev_entry(const char *name,DIR **dir,uchar **dn,ushort **ent)
{
	uchar *ent_dir;
	ushort *entry;
	int r;
	DIR_ENT *p=NULL;


	for(ent_dir=&dir[0]->entry_dir,entry=&dir[0]->entry_top;*entry!=0;)
	{
		splay(ent_dir,entry,dir);
		p=ENTRY(*entry,dir[*ent_dir]);
		r=cmp_path(p->name,name);
		if(r==-1)
		{
			++p->low_num;
			ent_dir=&p->low_dir;
			entry=&p->low;
		}
		else if(r==1)
		{
			++p->high_num;
			ent_dir=&p->high_dir;
			entry=&p->high;
		}
		else return -1;
	}

	*dn=ent_dir;
	*ent=entry;

	return 0;
}


/*
 * ディレクトリ内の空きエントリーを取得して、引数のポインターに代入する。
 * parameters : name size,directory pointer,directory buffer number pointer,entry offset pointer
 * return : directory entry pointer or error=NULL
 */
DIR_ENT *get_empty_entry(int path_len,DIR **dir,uchar *dn,ushort *entry)
{
	enum{MIM_EMPTY_SIZE=sizeof(DIR_ENT)+sizeof(int)};		/* 空きエントリーで最小の残すサイズ。 */

	int entry_len;
	uchar i;
	ushort k,last;
	EMPTY_ENT *p,*q;


	/* 名前のサイズを確認。 */
	if(path_len>MAX_NAME_SIZE)return NULL;

	entry_len=ROUNDUP(path_len+1,sizeof(int))+sizeof(DIR_ENT);

	/* 空きエントリーを検索。 */
	last=(uint)dir[0]->empty_prev-(uint)dir[0];
	for(i=0;dir[i]!=NULL;++i)
	{
		for(k=dir[i]->empty_next;k!=last;k=p->next)
		{
			p=EMPTY(k,dir[i]);
			if(p->size>entry_len)
			{
				if(p->size>entry_len+MIM_EMPTY_SIZE)
				{
					q=EMPTY(k+entry_len,dir[i]);
					q->prev=p->prev;
					q->next=p->next;
					EMPTY(q->prev,dir[i])->next+=entry_len;
					EMPTY(q->next,dir[i])->prev+=entry_len;
					q->size=p->size-entry_len;
					((DIR_ENT*)p)->size=entry_len;
				}
				else
				{
					EMPTY(p->prev,dir[i])->next=p->next;
					EMPTY(p->next,dir[i])->prev=p->prev;
					((DIR_ENT*)p)->size=p->size;
				}

				*dn=i;
				*entry=k;
				memset(p,0,sizeof(DIR_ENT));

				return (DIR_ENT*)p;
			}
		}
	}

	return NULL;
}


/*
 * 空きエントリーを追加する。
 * parameters :
 * return :
 */
void add_empty_entry(ushort entry,DIR *dir)
{
	ushort i,last;
	EMPTY_ENT *p,*q;


	i=dir->empty_next;
	p=(EMPTY_ENT*)&dir->empty_prev;

	/* オフセット順に空きエントリーのリンクを探す。 */
	for(last=(uint)&dir->empty_prev-(uint)dir;i!=last;i=p->next)
	{
		p=EMPTY(i,dir);
		if(i>entry)
		{
			/* 範囲が続いていれば結合する。 */
			if(i+p->size==entry)
			{
				p->size+=ENTRY(entry,dir)->size;
				return;
			}
			else break;
		}
	}

	/* 空きエントリーリンクに加える。 */
	q=EMPTY(entry,dir);
	q->size=((DIR_ENT*)q)->size;
	q->next=p->next;
	q->prev=(uint)p-(uint)dir;
	p->next=entry;
	EMPTY(q->next,dir)->prev=entry;
}


/*
 * エントリー数の少ない方へ回転させ、二分木内でエントリーを下降させて、リンクから削除する。
 * parameters :
 * return : 0 or error=-1
 */
int del_entry(const char *name,DIR **dir)
{
	uchar *ent_dir,i;
	ushort *entry,k;
	int r;
	DIR_ENT *p,*q;


	ent_dir=&dir[0]->entry_dir;
	entry=&dir[0]->entry_top;

	/* エントリーを検索する。 */
	for(;;)
	{
		if(*entry==0)return -1;

		p=ENTRY(*entry,dir[*ent_dir]);
		r=cmp_path(p->name,name);
		if(r==-1)
		{
			--p->low_num;
			ent_dir=&p->low_dir;
			entry=&p->low;
		}
		else if(r==1)
		{
			--p->high_num;
			ent_dir=&p->high_dir;
			entry=&p->high;
		}
		else break;
	}

	/* 下降させる。 */
	while(p->low_num*p->high_num)
	{
		if(p->low_num<p->high_num)
		{
			round_low(ent_dir,entry,dir);
			q=ENTRY(*entry,dir[*ent_dir]);
			ent_dir=&q->low_dir;
			entry=&q->low;
		}
		else
		{
			round_high(ent_dir,entry,dir);
			q=ENTRY(*entry,dir[*ent_dir]);
			ent_dir=&q->high_dir;
			entry=&q->high;
		}
	}

	/* 削除 */
	i=*ent_dir;
	k=*entry;
	*ent_dir=p->low_dir+p->high_dir;
	*entry=p->low+p->high;
	if(*entry!=dir[0]->entry_top)*(entry+2)=p->low_num+p->high_num;	/* DIR_ENT構造体の要素の配置に依存する。 */
	add_empty_entry(k,dir[i]);

	return 0;
}


/*
 * ブロックバッファに新規のディレクトリを作成する。
 * parameters : directory buffer,secter
 */
void make_dir(void *dir,uint secter)
{
	EMPTY_ENT *empty;


	((DIR*)dir)->my_secter=secter;
	((DIR*)dir)->next_dir=0;
	((DIR*)dir)->empty_prev=sizeof(DIR);
	((DIR*)dir)->empty_next=sizeof(DIR);
	((DIR*)dir)->entry_dir=0;
	((DIR*)dir)->entry_top=0;
	((DIR*)dir)->size=BLOCK_SIZE;
	((DIR*)dir)->owner_id=0;		/* 未設定 */
	((DIR*)dir)->group_id=0;		/* 未設定 */
	((DIR*)dir)->access_time=0;		/* 未設定 */
	((DIR*)dir)->create_time=0;		/* 未設定 */
	((DIR*)dir)->modificat_time=0;	/* 未設定 */

	/* 空きエントリーの設定。 */
	empty=(EMPTY_ENT*)((DIR*)dir)->entry;
	empty->prev=(uint)&((DIR*)dir)->empty_prev-(uint)dir;
	empty->next=empty->prev;
	empty->size=BLOCK_SIZE-sizeof(DIR);
}


/*
 * パスのディレクトリエントリーを探す。
 * parameters : path,device inode,directory block,directory pointer
 * return : directory entry or error=NULL
 */
DIR_ENT *search_dir_entry(const char *orig_path,int din,uint dir_blk,DIR **dir)
{
	const char *path=orig_path;
	int secters=super_blk[din]->secters;
	DIR_ENT *entry;
	int i;
	uint next_dir;


	if(*orig_path=='\0')return NULL;

	if((dir[0]=(DIR*)kmalloc(BLOCK_SIZE))==NULL)return NULL;
	for(;;)
	{
		/* ディレクトリをバッファに読み込む。 */
		if(read_cache(din,dir[0],super_blk[din]->secters,dir_blk)!=super_blk[din]->secters)return NULL;
		for(i=1,next_dir=dir[0]->next_dir;next_dir!=0;next_dir=dir[i++]->next_dir)
		{
			if(dir[i]==NULL)
				if((dir[i-1]=(DIR*)kmalloc(BLOCK_SIZE))==NULL)return NULL;
			if(read_cache(din,dir[0],secters,next_dir)!=secters)return NULL;
		}

		/* ディレクトリを検索。 */
		if((entry=search_same_entry(&path,dir))==NULL)return NULL;
		if(*path=='\0')break;
		if(entry->type!=DIRECTORY)return NULL;
		dir_blk=entry->secter;
	}

	return entry;
}


/*
 * パス名のディレクトリエントリーを取得する。エントリーにはパス名も代入される。
 * parameters : path,device inode,directory block,directory pointer
 * return : directory entry or error=NULL
 */
DIR_ENT *get_new_dir_entry(const char *orig_path,int din,uint dir_blk,DIR **dir)
{
	const char *path;
	const char *top;
	uchar **dn;
	ushort **ent;
	int secters=super_blk[din]->secters;
	DIR_ENT *entry;
	uint next_dir;
	int i,s;
	
	
	if(*orig_path=='\0')return NULL;

	/* 最後のパス名の先頭を。 */
	top=orig_path;
	for(path=orig_path;*path!='\0';++path)
		if(*path=='/')top=path+1;

	if((dir[0]=(DIR*)kmalloc(BLOCK_SIZE))==NULL)return NULL;
	path=orig_path;
	for(;;)
	{
		/* ディレクトリをバッファに読み込む。 */
		if(read_cache(din,dir[0],super_blk[din]->secters,dir_blk)!=super_blk[din]->secters)return NULL;
		for(i=1,next_dir=dir[0]->next_dir;next_dir!=0;next_dir=dir[i++]->next_dir)
		{
			if(dir[i]==NULL)
				if((dir[i-1]=(DIR*)kmalloc(BLOCK_SIZE))==NULL)return NULL;
			if(read_cache(din,dir[0],secters,next_dir)!=secters)return NULL;
		}
		
		if(path==top)break;

		/* ディレクトリを検索。 */
		if((entry=search_same_entry(&path,dir))==NULL)return NULL;
		if(entry->type!=DIRECTORY)return NULL;
		dir_blk=entry->secter;
	}

	/* エントリーを追加する。 */
	if(search_prev_entry(path,dir,dn,ent)==-1)return NULL;
	s=strlen(path);
	if((entry=get_empty_entry(s,dir,*dn,*ent))==NULL)return NULL;
	memcpy(entry->name,path,s+1);
/********************************************************************/
	printk("empty_blk[%d]->current=%x\n",din,empty_blk[din]->current);
/********************************************************************/
	return entry;
}


/************************************************************************************
 *
 * inode functions
 *
 ************************************************************************************/

/*
 * inodeからバッファにブロックデータを読み込む。
 * parameters : device inode,転送先バッファ,転送サイズ(byte),開始オフセット,inodeバッファポインタ
 * return : 読み込みバイト数 or error=-1
 */
int read_from_inode(int din,void *buf,size_t size,int begin,uint *inode)
{
	char *rbuf=NULL;
	int rest=size;
	int secters=super_blk[din]->secters;
	int i,s,t,last;


	i=begin/BLOCK_SIZE;

	/* 最初のブロックサイズ未満の転送。 */
	if((s=begin%BLOCK_SIZE)!=0)
	{
		if((rbuf=(char*)kmalloc(BLOCK_SIZE))==NULL)return -1;
		if(read_cache(din,rbuf,secters,inode[i])!=secters)goto ERR;
		if((t=BLOCK_SIZE-s)>size)t=size;
		memcpy(buf,rbuf+s,t);
		(char*)buf+=t;
		++i;
		rest-=t;
	}

	if(rest>(BLOCK_SIZE/sizeof(uint)-i)*BLOCK_SIZE)
	{
		rest-=(BLOCK_SIZE/sizeof(uint)-i)*BLOCK_SIZE;
		for(last=BLOCK_SIZE/sizeof(uint);i<last;++i)
		{
			if(read_cache(din,buf,secters,inode[i])!=secters)goto ERR;
			(char*)buf+=BLOCK_SIZE;
		}
	}
	else
	{
		for(;rest<BLOCK_SIZE;rest-=BLOCK_SIZE)
		{
			if(read_cache(din,buf,secters,inode[i++])!=secters)goto ERR;
			(char*)buf+=BLOCK_SIZE;
		}

		/* 残りがブロックサイズ未満の転送。 */
		if(rest!=0)
		{
			if(rbuf==NULL)
				if((rbuf=(char*)kmalloc(BLOCK_SIZE))==NULL)return -1;
			if(read_cache(din,rbuf,secters,inode[i])!=secters)goto ERR;
			memcpy(buf,rbuf,rest);
		}
	}

	kfree(rbuf);

	return size-rest;

ERR:
	kfree(rbuf);

	return -1;
}


/*
 * inodeからバッファにブロックデータを書き込む。
 * parameters : device inode,転送先バッファ,転送サイズ(byte),開始オフセット,inodeバッファポインタ
 * return : 書き込みバイト数 or error=-1
 */
int write_to_inode(int din,void *buf,size_t size,int begin,uint *inode)
{
	char *rbuf=NULL;
	int rest=size;
	int secters=super_blk[din]->secters;
	int i,s,t,last;


	i=begin/BLOCK_SIZE;

	/* 最初のブロックサイズ未満の転送。 */
	if((s=begin%BLOCK_SIZE)!=0)
	{
		if((rbuf=(char*)kmalloc(BLOCK_SIZE))==NULL)return -1;
		if(read_cache(din,rbuf,secters,inode[i])!=secters)goto ERR;
		if((t=BLOCK_SIZE-s)>size)t=size;
		memcpy(rbuf+s,buf,t);
		if(write_cache(din,rbuf,secters,inode[i])!=secters)goto ERR;
		(char*)buf+=t;
		++i;
		rest-=t;
	}

	if(rest>(BLOCK_SIZE/sizeof(uint)-i)*BLOCK_SIZE)
	{
		rest-=(BLOCK_SIZE/sizeof(uint)-i)*BLOCK_SIZE;
		for(last=BLOCK_SIZE/sizeof(uint);i<last;++i)
		{
			if(write_cache(din,buf,secters,inode[i])!=secters)goto ERR;
			(char*)buf+=BLOCK_SIZE;
		}
	}
	else
	{
		for(;rest<BLOCK_SIZE;rest-=BLOCK_SIZE)
		{
			if(write_cache(din,buf,secters,inode[i++])!=secters)goto ERR;
			(char*)buf+=BLOCK_SIZE;
		}

		/* 残りがブロックサイズ未満の転送。 */
		if(rest!=0)
		{
			if(rbuf==NULL)
				if((rbuf=(char*)kmalloc(BLOCK_SIZE))==NULL)return -1;
			memcpy(rbuf,buf,rest);
			if(write_cache(din,rbuf,secters,inode[i])!=secters)goto ERR;
		}
	}

	kfree(rbuf);

	return size-rest;

ERR:
	kfree(rbuf);

	return -1;
}


/*
 * 追加でブロックデータを書き込む。
 * parameters : device inode,転送先バッファ,転送サイズ(byte),開始オフセット,inodeバッファポインタ
 * return : 書き込みバイト数 or error=-1
 */
int write_add(int din,void *buf,size_t size,int begin,uint *inode)
{
	void *wbuf;
	int rest;
	int secters=super_blk[din]->secters;
	int i;


	if(size>BLOCK_SIZE/sizeof(uint)*BLOCK_SIZE-begin)
	{
		for(i=begin/BLOCK_SIZE;i<BLOCK_SIZE/sizeof(uint);++i)
		{
			if((inode[i]=get_empty_blk(din))==0)return -1;
			if(write_cache(din,buf,secters,inode[i])!=secters)return -1;
			(char*)buf+=BLOCK_SIZE;
		}

		return BLOCK_SIZE/sizeof(uint)*BLOCK_SIZE-begin;
	}
	else
	{
		i=begin/BLOCK_SIZE;
		for(rest=size;rest<BLOCK_SIZE;rest-=BLOCK_SIZE)
		{
			if((inode[i++]=get_empty_blk(din))==0)return -1;
			if(write_cache(din,buf,secters,inode[i])!=secters)return -1;
			(char*)buf+=BLOCK_SIZE;
		}
		if((wbuf=kmalloc(BLOCK_SIZE))==NULL)return -1;
		memcpy(wbuf,buf,size);
		if(write_cache(din,wbuf,secters,inode[i])!=secters)return -1;
		kfree(wbuf);

		return size;
	}
}


/*
 * inodeからバッファにブロックデータを読み込む。
 * parameters : device inode,転送先バッファ,転送サイズ(byte),開始オフセット,間接参照ノード,
 *              inodeバッファポインタ(空きインデックスは必ず0)
 * return : 読み込みバイト数 or error=-1
 */
int read_indirect_inode(int din,void *buf,size_t size,int begin,int node,uint *inode)
{
	uint *_inode;
	int rest=size;
	int secters=super_blk[din]->secters;
	int i,s;


	if((_inode=(uint*)kmalloc(BLOCK_SIZE))==NULL)return -1;

	i=begin/indirect_size[node];

	if(node==1)
	{
		if(read_cache(din,_inode,secters,inode[i++])!=secters)goto ERR;
		if((s=read_from_inode(din,buf,size,begin,_inode))==-1)goto ERR;
		rest-=s;

		if(rest>(BLOCK_SIZE/sizeof(uint)-1)*indirect_size[1])
		{
			rest-=(BLOCK_SIZE/sizeof(uint)-1)*indirect_size[1];
			for(;i<BLOCK_SIZE/sizeof(uint);++i)
			{
				if(read_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
				if(read_from_inode(din,buf,indirect_size[1],0,_inode)==-1)goto ERR;
			}
		}
		else
		{
			for(;rest>0;rest-=indirect_size[1])
			{
				if(read_cache(din,_inode,secters,inode[i++])!=secters)goto ERR;
				if(read_from_inode(din,buf,rest,0,_inode)==-1)goto ERR;
			}
			rest=0;
		}
	}
	else
	{
		if(read_cache(din,_inode,secters,inode[i++])!=secters)goto ERR;
		if((s=read_indirect_inode(din,buf,size,begin,node-1,_inode))==-1)goto ERR;
		rest-=s;

		if(rest>(BLOCK_SIZE/sizeof(uint)-1)*indirect_size[node])
		{
			rest-=(BLOCK_SIZE/sizeof(uint)-1)*indirect_size[node];
			for(;i<BLOCK_SIZE/sizeof(uint);++i)
			{
				if(read_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
				if(read_indirect_inode(din,buf,indirect_size[node],0,node-1,_inode)==-1)goto ERR;
			}
		}
		else
		{
			for(;rest>0;rest-=indirect_size[node])
			{
				if(read_cache(din,_inode,secters,inode[i++])!=secters)goto ERR;
				if(read_indirect_inode(din,buf,rest,0,node-1,_inode)==-1)goto ERR;
			}
			rest=0;
		}
	}

	kfree(_inode);

	return size-rest;

ERR:
	kfree(_inode);

	return -1;
}


/*
 * 間接参照から書き込み追加する。
 * parameters : device inode,転送先バッファ,転送サイズ(byte),開始オフセット,間接参照ノード,
 *              inodeバッファポインタ(空きインデックスは必ず0),現在のファイルサイズ
 * return : 読み込みバイト数 or error=-1
 */
int write_indirect_inode(int din,void *buf,size_t size,int begin,int node,uint *inode,size_t fsize)
{
	uint *_inode;
	int secters=super_blk[din]->secters;
	int i,s,rest,tmp;


	if((_inode=(uint*)kmalloc(BLOCK_SIZE))==NULL)return -1;

	i=begin/indirect_size[node];

	if(node==1)
	{
		/* 既存ブロック書き込み。 */
		tmp=rest=(size>fsize)?fsize:size;

		if(read_cache(din,_inode,secters,inode[i++])!=secters)goto ERR;
		if((s=write_to_inode(din,buf,size,begin,_inode))==-1)goto ERR;
		rest-=s;

		for(;rest>=indirect_size[1];++i,rest-=indirect_size[1])
		{
			if(i==BLOCK_SIZE/sizeof(uint))goto END;

			if(read_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
			if(write_to_inode(din,buf,indirect_size[1],0,_inode)==-1)goto ERR;
		}

		if(read_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
		if(write_to_inode(din,buf,rest,0,_inode)==-1)goto ERR;

		/* 追加ブロック書き込み。 */
		tmp=size;
		rest=size-fsize;

		if(rest==0)goto END;

		if((s=write_add(din,buf,rest,rest%indirect_size[1],_inode))==-1)goto ERR;
		if(write_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
		rest-=s;

		for(++i;rest>0;++i,rest-=indirect_size[1])
		{
			if(i==BLOCK_SIZE/sizeof(uint))goto END;

			if((inode[i]=get_empty_blk(din))==0)goto ERR;
			memset(_inode,0,BLOCK_SIZE);
			if(write_add(din,buf,rest,0,_inode)==-1)goto ERR;
			if(write_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
		}
		rest=0;
	}
	else
	{
		/* 既存ブロック書き込み。 */
		tmp=rest=size;

		if(read_cache(din,_inode,secters,inode[i++])!=secters)goto ERR;
		if((s=write_indirect_inode(din,buf,size,begin,node-1,_inode,fsize))==-1)goto ERR;
		rest-=s;
		fsize-=s;

		for(;rest>=indirect_size[node];rest-=indirect_size[node])
		{
			if(i==BLOCK_SIZE/sizeof(uint))goto END;

			if(read_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
			if(write_indirect_inode(din,buf,indirect_size[node],0,node-1,_inode,fsize)==-1)goto ERR;

			++i;
			fsize-=indirect_size[node];
		}

		if(read_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
		if(write_indirect_inode(din,buf,indirect_size[node],0,node-1,_inode,fsize)==-1)goto ERR;

		/* 追加ブロック書き込み。 */
		tmp=size;
		rest=size-fsize;

		if((s=write_indirect_inode(din,buf,rest,rest%indirect_size[node],node-1,_inode,0))==-1)goto ERR;
		if(write_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
		rest-=s;
		++i;

		for(;rest>0;rest-=indirect_size[1])
		{
			if(i==BLOCK_SIZE/sizeof(uint))goto END;

			if((inode[i]=get_empty_blk(din))==0)goto ERR;
			memset(_inode,0,BLOCK_SIZE);
			if(write_indirect_inode(din,buf,indirect_size[node],0,node-1,_inode,0)==-1)goto ERR;
			if(write_cache(din,_inode,secters,inode[i])!=secters)goto ERR;
			++i;
		}
		rest=0;
	}
END:
	kfree(_inode);

	return tmp-rest;

ERR:
	kfree(_inode);

	return -1;
}


/*
 * ブロックバッファに新規のinodeを作成する。
 * parameters : buffer,secter,fiel type
 */
void make_inode(INODE *inode,uint secter,int type)
{
	memset(inode,0,BLOCK_SIZE);
	inode->my_secter=secter;
	inode->file_type=type;
}


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

uint mount(int din)
{
	void *buf;
	int secters;			/* BLOCK_SIZE/secter */
	DEV_STAT dev_stat;


	/* すでにマウントされているか確認 */
	if(super_blk[din]!=NULL)return 0;

	/* Read super block */
	if(get_dev_stat(din,&dev_stat)==-1)return 0;
	secters=BLOCK_SIZE/dev_stat.sect_size;
	if((buf=kmalloc(BLOCK_SIZE))==NULL)return 0;
	if(read_direct(din,buf,secters,SBLOCK_SECTER)!=secters)return 0;
	if(((SUPER_BLOCK*)buf)->magic_number!=SBLOCK_MAGIC)return 0;
	super_blk[din]=(SUPER_BLOCK*)buf;

	/* Read empty block */
	if((buf=kmalloc(BLOCK_SIZE))==NULL)goto ERR;
	if(read_direct(din,buf,secters,super_blk[din]->empty_blk)!=secters)goto ERR;
	empty_blk[din]=(ENPTY_BLOCK*)buf;

	return super_blk[din]->root_blk;

ERR:
	kfree(super_blk[din]);
	super_blk[din]=NULL;
	kfree(empty_blk[din]);
	empty_blk[din]=NULL;

	return 0;
}


int umount(int din)
{
	int secters=super_blk[din]->secters;
	
	
	/* Write empty block */
	if((super_blk[din]->empty_blk=empty_blk[din]->empty[empty_blk[din]->current])!=0)
		if(write_direct(din,empty_blk[din],secters,super_blk[din]->empty_blk)!=secters)return -1;
	kfree(empty_blk[din]);
	empty_blk[din]=NULL;

	/* Write super block */
	if(write_direct(din,super_blk[din],secters,SBLOCK_SECTER)!=secters)return -1;
	kfree(super_blk[din]);
	super_blk[din]=NULL;

	return 0;
}


int open(const char *path,int din,uint dir_blk)
{
	int inode;
	DIR *dir[MAX_DIR_NUM+1];
	DIR_ENT *entry;
	int i;


	memset(dir,0,sizeof(DIR*)*(MAX_DIR_NUM+1));

	/* パスを検索。 */
	if((entry=search_dir_entry(path,din,dir_blk,dir))==NULL)inode=-1;
	else
	{
		if(entry->type==NORMAL_FILE)inode=entry->secter;
		else inode=-1;
	}

	/* ディレクトリバッファを開放する。 */
	for(i=0;dir[i]!=NULL;++i)kfree(dir[i]);

	return inode;
}


int close(int din,uint inode)
{
	return 0;
}


int read(int din,uint ind,void *buf,size_t size,size_t begin)
{
	INODE *inode;	/* inodeバッファ */


	if((inode=(INODE*)kmalloc(BLOCK_SIZE))==NULL)return -1;
	if(read_cache(din,inode,super_blk[din]->secters,ind)==super_blk[din]->secters)goto ERR;

	/* サイズの確認。 */
	if(begin>inode->size)goto ERR;
	if((size+begin)>inode->size)size=inode->size-begin;

	/* ファイルの読み込み。 */
	if(inode->indirect==0)
	{
		begin+=sizeof(INODE)/sizeof(uint)*indirect_size[0];
		if(read_from_inode(din,buf,size,begin,(uint*)inode)!=size)goto ERR;
	}
	else
	{
		begin+=sizeof(INODE)/sizeof(uint)*indirect_size[inode->indirect];
		if(read_indirect_inode(din,buf,size,begin,inode->indirect,(uint*)inode)!=size)goto ERR;
	}

	kfree(inode);

	return 0;

ERR:
	kfree(inode);

	return -1;
}


int write(int din,uint ind,void *buf,size_t size,size_t begin)
{
	uint *_inode=NULL;
	int secters=super_blk[din]->secters;
	INODE *inode;
	int i;


	if((inode=(INODE*)kmalloc(BLOCK_SIZE))==NULL)return -1;
	if(read_cache(din,inode,secters,ind)!=secters)goto ERR;

	/* 次の間接参照ノードに設定する。 */
	i=inode->indirect;
	if((begin+size)>(indirect_size[i]*((BLOCK_SIZE-sizeof(INODE))/sizeof(uint))))
	{
		if((_inode=(uint*)kmalloc(BLOCK_SIZE))==NULL)goto ERR;
		do
		{
			memcpy(_inode,inode->secter,BLOCK_SIZE-sizeof(INODE));
			memset((char*)_inode+(BLOCK_SIZE-sizeof(INODE)),0,sizeof(INODE));
			if((inode->secter[0]=get_empty_blk(din))==-1)goto ERR;
			if(write_cache(din,_inode,secters,inode->secter[0])!=secters)goto ERR;
			memset(&inode->secter[1],0,BLOCK_SIZE-sizeof(INODE)-sizeof(uint));
		}while((begin+size)>(indirect_size[++i]*((BLOCK_SIZE-sizeof(INODE))/sizeof(uint))));
		inode->indirect=i;
	}

	/* 書き込み。 */
	if(write_indirect_inode(din,buf,size,begin+(BLOCK_SIZE-sizeof(INODE))/sizeof(uint)*indirect_size[i],
		i,(uint*)inode,inode->size)!=size)goto ERR;
	if(write_cache(din,inode,secters,ind)!=secters)goto ERR;

	kfree(_inode);
	kfree(inode);

	return 0;
ERR:
	kfree(_inode);
	kfree(inode);

	return -1;
}


int mkdir(const char *path,int din,uint dir_blk)
{
	uint secter;
	int secters=super_blk[din]->secters;
	DIR *dir[MAX_DIR_NUM+1];
	DIR *newdir;
	DIR_ENT *entry;
	int i;


	memset(dir,0,(MAX_DIR_NUM+1)*sizeof(DIR*));

	/* 新しいディレクトリエントリーを取得する */
	if((entry=get_new_dir_entry(path,din,dir_blk,dir))==NULL)goto ERR1;

	/* ディレクトリエントリーを初期化する。 */
	if((secter=get_empty_blk(din))==0)goto ERR1;
	if((newdir=(DIR*)kmalloc(BLOCK_SIZE))==NULL)goto ERR2;
	make_dir(newdir,secter);
	if(write_cache(din,newdir,secters,secter)!=secters)
	{
		kfree(newdir);
		goto ERR2;
	}
	kfree(newdir);
	entry->secter=secter;
	entry->type=DIRECTORY;

	/* 現在のディレクトリを書き戻す。 */
	for(i=0;dir[i]!=NULL;++i)
		if(write_cache(din,dir[i],secters,dir[i]->my_secter)!=secters)goto ERR2;

	for(i=0;dir[i]!=NULL;++i)kfree(dir[i]);

	return 0;

ERR2:
	release_blk(din,secter);
ERR1:
	for(i=0;dir[i]!=NULL;++i)kfree(dir[i]);

	return -1;
}


int creat(const char *path,int din,uint dir_blk)
{
	int secter;
	int secters=super_blk[din]->secters;
	DIR *dir[MAX_DIR_NUM+1];
	INODE *newinode;
	DIR_ENT *entry;
	int i;


	memset(dir,0,(MAX_DIR_NUM+1)*sizeof(DIR*));

	/* 新しいディレクトリエントリーを取得する */
	if((entry=get_new_dir_entry(path,din,dir_blk,dir))!=NULL)goto ERR1;

	/* ディレクトリエントリーを初期化する。 */
	if((secter=get_empty_blk(din))==0)goto ERR1;
	if((newinode=(INODE*)kmalloc(BLOCK_SIZE))==NULL)goto ERR2;
	make_inode(newinode,secter,NORMAL_FILE);
	if(write_cache(din,newinode,secters,secter)!=secters)
	{
		kfree(newinode);
		goto ERR2;
	}
	kfree(newinode);
	entry->secter=secter;
	entry->type=NORMAL_FILE;

	/* 現在のディレクトリを書き戻す。 */
	for(i=0;dir[i]!=NULL;++i)
		if(write_cache(din,dir[i],secters,dir[i]->my_secter)!=secters)goto ERR2;

	for(i=0;dir[i]!=NULL;++i)kfree(dir[i]);

	return 0;

ERR2:
	release_blk(din,secter);
ERR1:
	for(i=0;dir[i]!=NULL;++i)kfree(dir[i]);

	return -1;
}


uint opendir(const char *path,int din,uint dir_blk)
{
	int newdir;
	DIR *dir[MAX_DIR_NUM+1];
	DIR_ENT *entry;
	int i;


	/* ディレクトリバッファの初期化。 */
	memset(dir,0,sizeof(DIR*)*(MAX_DIR_NUM+1));

	/* パスを検索。 */
	if((entry=search_dir_entry(path,din,dir_blk,dir))==NULL)newdir=-1;
	else
	{
		if(entry->type==DIRECTORY)newdir=entry->secter;
		else newdir=-1;
	}

	/* ディレクトリバッファを開放する。 */
	for(i=0;dir[i]!=NULL;++i)kfree(dir[i]);

	return newdir;
}

/*********************************** 未完 ***************************************/
int ioctr(int din,uint inode,int cmd,void *param)
{
	return 0;
}


int rename(int din,uint inode,const char *name)
{
	return 0;
}
/************************************** テスト *******************************************/
void test_orig_fs()
{
	const char *home="home";
	int fd,din;
	DIR *dir[MAX_DIR_NUM+1];


	memset(dir,0,(MAX_DIR_NUM+1)*sizeof(DIR*));

	if(init_orig_fs()==-1)
	{
		printk("Failed init_orig_fs\n");
		return;
	}

	if(sys_mount("/dev/hdb7","orig_fs","/")==-1)
	{
		printk("Failed sys_mount\n");
		return;
	}

	if((fd=sys_open("/dev/hdb7",0))==-1)return;
	if((din=get_inode(fd))==-1)return;

	printk("mkdir()=%d\n",mkdir(home,din,super_blk[din]->root_blk));

	printk("search_dir_entry()=%x\n",search_dir_entry(home,din,super_blk[din]->root_blk,dir));
	
	printk("umount()=%d\n",umount(din));
}
