/*
 * fs.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * 仮想ファイルシステム。
 * ファイルシステム管理。
 */


#include <types.h>
#include <config.h>
#include <limits.h>
#include <lib.h>
#include <mm.h>
#include <proc.h>
#include <lock.h>
#include <errno.h>
#include <interrupt.h>
#include <time.h>
#include <signal.h>
#include <device.h>
#include <net/net.h>
#include <share/utime.h>
#include <sys/resource.h>
#include <fs.h>
#include <test.h>
#include <debug.h>


//#define DEBUG_FS 1

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

//	#define CALL_PRINT()	PRINT_ERR("")
	#define CALL_PRINT()

/***************************************************************************************
 *
 * 仮想ファイルシステム
 *
 * ○ マウント時・カレントディレクトリ変更時・ファイルオープン時・ファイル実行時に追加する。
 * ○ エントリーは、"."+".."+名前の値順に並べる。
 * ○ エントリーリンクの最後は最初のエントリーに連結する。
 * ○ ディレクトリはマウントされたもの以外はデバイスブロック要素は空。
 * ○ システムコール操作で検索した末端エントリーは、そのシステムコール中に削除されないよう、
 * 　 カウンタを増やし、そのシステムコールの最後でカウンタを減らす。
 *
 ***************************************************************************************/

enum{
	VFS_MAX_NAME_SIZE = PATH_MAX,		/* 仮想ファイルシステムの最大名前サイズ。 */
};


typedef struct{
	char *path;
	VENTRY *vent;
}SEARCH_PARAM;


/* PUBLIC ルートエントリー。 */
static char _rootEnt[sizeof(VENTRY)+1];
static VENTRY *rootEnt = (VENTRY*)&_rootEnt;

/* PUBLIC devディレクトリエントリー。 */
static char _devEnt[sizeof(VENTRY)+1];
static VENTRY *devEnt=(VENTRY*)&_devEnt;

/* PUBLIC 仮想ファイルシステムスピンロックゲート。 */
static int spinLock=0;


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


/*
 * エントリーリンクからエントリーを検索する。
 * 引数のエントリーアドレスポインターには見つかったエントリーまたは、
 * 検索した最後のエントリーアドレスをバッファに代入する。
 * parameters : パスアドレス,ディレクトリエントリーアドレスポインター,entry buffer
 * return : next path or failed=NULL
 */
STATIC char *searchEntry(char *path,VENTRY *entry,VENTRY **pEntry)
{
	int rest;
	VENTRY *last;



	if (cmpPath(".",path,1) == 0)
	{
		*pEntry = entry;
		if (path[1] == '/')
			return &path[2];
		else
			return &path[1];
	}
	if (cmpPath("..",path,2) == 0)
	{
		*pEntry = entry->parent;
		if (path[2] == '/')
			return &path[3];
		else
			return &path[2];
	}

	if ((last = entry = entry->child) == NULL)
	{
		*pEntry = NULL;
		return NULL;
	}

	switch (rest = cmpPath(entry->name,path,entry->len))
	{
		case -1:
			for (entry = entry->next; entry != last; entry = entry->next)
				if ((rest = cmpPath(entry->name,path,entry->len)) != -1)
					break;
			break;
		case 1:
			*pEntry = NULL;
			return NULL;
	}

	*pEntry = entry;

	if (rest == 0)
	{
		for (path += entry->len; *path == '/'; ++path);
		return path;
	}

	return NULL;
}


/*
 * エントリーを削除する。
 * parameters : entry addres
 * return : 0 or error number
 */
STATIC int delEntry(VENTRY *entry)
{
	char name[VFS_MAX_NAME_SIZE],*pname;
	VENTRY *parent;


	if (entry->refCount > 0)
		return -EBUSY;
	if (entry->child != NULL)
		return -EBUSY;

	parent = entry->parent;

	if (entry->next == entry)
		parent->child = NULL;
	else
	{
		if (parent->child == entry)
			parent->child = entry->next;
		entry->prev->next = entry->next;
		entry->next->prev = entry->prev;
	}

	/* ファイルの削除 */
	if (entry->link-- <= 0)
	{
		name[VFS_MAX_NAME_SIZE-1] = '\0';
		pname = &name[VFS_MAX_NAME_SIZE-1];
		memcpy(pname -= entry->len,entry->name,entry->len);
		for (parent = entry->parent; parent->inodeObj == NULL; parent = parent->parent)
		{
			if (pname + parent->len + 1 < name)
				goto EXIT;
			*--pname = '/';
			memcpy(pname -= parent->len,parent->name,parent->len);
		}
		entry->fs->delete(pname, parent->din, parent->inodeObj, S_IFREG);
	}
EXIT:
	kfree(entry->wait);
	kfree(entry);

	return 0;
}


/*
 * エントリーを遡って削除する。
 * parameters : 削除エントリー
 */
STATIC void delUpEntry(VENTRY *entry)
{
	VENTRY *parent;

	for(;;)
	{
		parent = entry->parent;
		if (delEntry(entry) != 0)
			break;
		entry=parent;
	}
}


/*
 * ディレクトリ内マウントエントリーの検索。
 * parameters : VENTRY,struct statfs,struct statfs領域数,struct statfsある無しフラグ
 */
STATIC int getFsstat(VENTRY *vent,struct statfs *buf);
STATIC int searchMountDir(VENTRY *vent,struct statfs *buf,int buf_cnt,int flag)
{
	int rest,count;
	VENTRY *vent_begin;


	if(buf_cnt<=0)return 0;
	vent_begin=vent;
	count=buf_cnt;

	do
	{
		/* マウントあり。 */
		if(vent->mount!=NULL)
		{
			while(vent->mount!=NULL)vent=vent->mount;
			if(flag)
				if(getFsstat(vent,buf)==0)
				{
					buf+=1;
					count-=1;
				}	
		}

		if(vent->child!=NULL)
		{
			rest=searchMountDir(vent->child,buf,count,flag);
			buf+=rest;
			count-=rest;
		}
	}
	while((vent=vent->next)!=vent_begin);

	return buf_cnt-count;
}


//================================== PROTECTED ==========================================


/*
 * パス名のエントリーを検索する。
 * 最初の引数には呼び出し元から値が与えられ、マウントされたディレクトリがあればそこに代入される。
 * 次の引数はパスの最終のエントリーが代入される。
 * parameters : start search parmeters struct,last search parmeters struct
 * return : 0 or error number
 */
STATIC int searchPath(SEARCH_PARAM *start,SEARCH_PARAM *end)
{
	char *pPath,*retPath;
	VENTRY *entry,*pEntry;


	entry = start->vent;
	for (pPath = start->path; *pPath != '\0';)
	{
		if (entry->type != S_IFDIR)
			return -ENOTDIR;
		if ((retPath = searchEntry(pPath,entry,&pEntry)) == NULL)
			break;
		pPath = retPath;
		entry = pEntry;
		if (entry->mount != NULL)
		{
			start->path = pPath;
			start->vent = entry->mount;
			entry = entry->mount;
		}
	}

	end->path = pPath;
	end->vent = entry;

	return 0;
}


/*
 * パスの先頭ノード名のエントリーをリンクに追加する。
 * parameters : path pointer,parent entry,file type
 * return : new entry address
 */
STATIC VENTRY *addEntry(char *path,VENTRY *entry,int type)
{
	int len;
	VENTRY *next_ent,*new_ent;


	/* エントリーを検索。 */
	if (searchEntry(path,entry,&next_ent) != NULL)
		return NULL;

	/* エントリーの作成。 */
	len = pathLen(path);
	new_ent = kmalloc(sizeof(VENTRY) + len);
	if (new_ent == NULL)
		return NULL;
	memset(new_ent,0,sizeof(VENTRY));
	new_ent->fs = entry->fs;
	new_ent->din = entry->din;
	new_ent->type = type;
	new_ent->parent = entry;
	new_ent->len = len;
	new_ent->link = 1;
	memcpy(new_ent->name,path,len);

	/* リンクに追加する。 */
	if (next_ent != NULL)
	{
		new_ent->prev = next_ent->prev;
		new_ent->next = next_ent;
		next_ent->prev->next = new_ent;
		next_ent->prev = new_ent;
	}
	else if (entry->child != NULL)
	{
		next_ent = entry->child;
		new_ent->prev = next_ent->prev;
		new_ent->next = next_ent;
		next_ent->prev->next = new_ent;
		next_ent->prev = new_ent;
		entry->child = new_ent;
	}
	else
	{
		new_ent->prev = new_ent;
		new_ent->next = new_ent;
		entry->child = new_ent;
	}

	return new_ent;
}


/*
 * エントリーの参照カウンタを1増やす。
 * parameters : 仮想エントリー
 */
STATIC INLINE void linkVentry(VENTRY *vent)
{
	vent->refCount += 1;
	vent->link += 1;
}


/*
 * エントリーの参照カウンタを減らし、カウンタ0なら上位ノードエントリーも遡って削除する。
 * parameters : 仮想エントリー
 */
STATIC INLINE int unlinkVentry(VENTRY *vent)
{
	int cnt;

	if ((cnt = --vent->refCount) <= 0)
	{
		delUpEntry(vent);
		return 0;
	}

	return cnt;
}


/*
 * エントリーのリンクカウントを減らす
 */
STATIC INLINE void unlinkVentryLink(VENTRY *vent)
{
	--vent->link;
}


/*
 * エントリーをパス順に追加する。
 * 注意！ pathの最初がNULL文字でないこと。
 * parameters : path,仮想エントリー
 * return : last entry or error number
 */
STATIC int mkEntry(char *path, VENTRY *vent)
{
	for(;;++path){
		vent = addEntry(path, vent, S_IFDIR);
		if (vent == NULL){
			return -ENOENT;
		}
		path+=vent->len;

		if (*path=='\0'){
			break;
		}
	}
	vent->refCount = 1;
	vent->link = 1;

	return (int)vent;
}


/*
 * エントリーに新しいエントリーをマウントする。
 * parameters : 被マウントエントリー,filesystem,device inode,root inode object
 * return : 0 or error number
 */
STATIC int mountEntry(VENTRY *entry, FS *fs, int din, void *rootInodeObj)
{
	VENTRY *mount_ent;


	/* すでにマウントされているか。 */
	if (entry->mount != NULL)
		return -EBUSY;

	/* マウントエントリーの作成。 */
	if ((mount_ent = kmalloc(sizeof(VENTRY) + entry->len)) == NULL)
		return -ENOMEM;
	memset(mount_ent,0,sizeof(VENTRY));
	mount_ent->next		= entry->next;
	mount_ent->prev		= entry->prev;
	mount_ent->inodeObj	= rootInodeObj;
	mount_ent->parent	= entry->parent;
	mount_ent->fs		= fs;
	mount_ent->din		= din;
	mount_ent->type		= S_IFDIR;
	mount_ent->len		= entry->len;
	memcpy(mount_ent->name,entry->name,entry->len);

	linkVentry(mount_ent);

	/* 被マウントエントリーにマウントする。 */
	linkVentry(entry);
	entry->mount = mount_ent;

	return 0;
}


/*
 * アンマウント。
 * parameters : 被マウントエントリー
 * return : error number
 */
STATIC int umountEntry(VENTRY *entry)
{
	FS *fs;
	VENTRY *mountVent;
	int rest;

	if ((mountVent = entry->mount) == NULL){
		return -ENOENT;
	}
	if (1 < mountVent->refCount){
		return -EBUSY;
	}
	if (mountVent->child != mountVent->child->next->next){
		return -EBUSY;
	}

	/* マウントエントリーの削除。 */
	fs = mountVent->fs;
	mountVent->refCount = 0;
	if ((rest = delEntry(mountVent)) < 0){
		return rest;
	}
	if ((rest = fs->umount(mountVent->din, mountVent->inodeObj)) < 0){
		return rest;
	}
	--entry->refCount;

	/* 被マウントエントリーを溯りながら削除。 */
	unlinkVentry(entry);

	return NOERR;
}


/*
 * パスをルートまで溯ってバッファにコピーする。
 * parameters : buffer,buffer size,仮想エントリー
 * return : write size or error=-1
 */
STATIC int copyCurrentPath(char *buf,size_t size,VENTRY *vent)
{
	int len;


	if(vent==rootEnt)
	{
		buf[0]='/';
		return 1;
	}
	else
	{
		if((len=copyCurrentPath(buf,size,vent->parent))==-1)return -1;
		if(len+vent->len+1>size)return -1;
		memcpy(&buf[len],vent->name,vent->len);
		buf[len+vent->len]='/';

		return len+vent->len+1;
	}
}


/*
 * 仮想エントリー情報を得る、後で開放が必要。
 * parameters : start search parmeters struct,last search parmeters struct
 * return : 0 or error number
 */
STATIC int getVentObj(SEARCH_PARAM *start,SEARCH_PARAM *end)
{
	int rest;


	enter_spinlock(&spinLock);
	{
		if ((rest = searchPath(start,end)) == 0)
			end->vent->refCount += 1;
	}
	exit_spinlock(&spinLock);

	return rest;
}


/*
 * 仮想エントリーの競合制御を設定する。
 */
STATIC void setVentObj(VENTRY *vent)
{
	enter_spinlock(&spinLock);
	{
		vent->refCount+=1;
	}
	exit_spinlock(&spinLock);
}


/*
 * 仮想エントリー情報の開放。
 */
STATIC void releaseVentObj(VENTRY *vent)
{
	enter_spinlock(&spinLock);
	{
		vent->refCount -= 1;
	}
	exit_spinlock(&spinLock);
}


/*
 * struct statfsに格納する。
 * return : 0 or error number
 */
STATIC int getFsstat(VENTRY *vent, struct statfs *buf)
{
	int rest;
	DEV_STAT devstat;

	if ((rest = vent->fs->statfs(vent->din, buf)) < 0){
		return rest;
	}
	get_devstat(vent->din,&devstat);

	if (devstat.prt_num > 0){
		sprintk(buf->f_mntfromname, "/dev/%s%d", devstat.name, devstat.prt_num);
	}
	else{
		sprintk(buf->f_mntfromname, "/dev/%s", devstat.name);
	}
	copyCurrentPath(buf->f_mntonname, MNAMELEN, vent);

	return 0;
}


/*
 * マウントエントリーを検索する。
 * parameters : struct statfs,buf count
 * return : struct statfsへ格納数 or マウント数（struct statfs=NULL）
 */
STATIC int searchMount(struct statfs *m_statfs, int c_statfs)
{
	int rest, flag;

	if (c_statfs <= 0){
		return 0;
	}

	flag = (m_statfs == NULL)? 0 : 1;
	
	/* rootファイルシステム。 */
	if(flag==1){
		getFsstat(rootEnt, m_statfs);
	}

	rest = searchMountDir(rootEnt->child, m_statfs + 1, c_statfs - 1, flag);

	return rest + 1;
}


/************************************************************************************
 *
 * ファイルシステム管理
 *
 ************************************************************************************/

/* PRIVATE */
STATIC FS fsInfoTop;


/*
 * PUBLIC
 * Search file system.
 * parameters : file system name
 * return : FS address or NULL
 */
STATIC FS *search_fs(const char *name)
{
	FS *p;


	for(p=fsInfoTop.next;p!=NULL;p=p->next)
		switch(strcmp(name,p->name))
		{
			case 0:
				return p;
			case -1:
				return NULL;
		}

	return NULL;
}


/*
 * GLOBAL
 * Regist filesystem.
 * parameters : Filesystem struct
 * return : 0 or Error=-1
 */
int regist_fs(FS *info)
{
	FS *p;


	p=&fsInfoTop;
	for(;p->next!=NULL;p=p->next)
		switch(strcmp(info->name,p->next->name))
		{
			case 0:
				return -EEXIST;
			case -1:
				goto EXIT;
		}
EXIT:
	info->next=p->next;
	p->next=info;

	return 0;
}


/************************************************************************************
 *
 * プロセスのファイル管理
 *
 ************************************************************************************/


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


/*
 * Get empty fd number.
 * 《注意》引数"min"は"OPEN_MAX"以上でないこと。
 * parameter : FILE_STRUCT,fd number
 * return : empty number or error number
 */
STATIC INLINE int getEmptyFdNum(FILE_STRUCT *fstruct, const int min)
{
	int num = min;

	for (num = min; fstruct->fd[num] != NULL;){
		if (OPEN_MAX <= ++num){
			num = 0;
		}
		if (num == min){
			return -ENFILE;
		}
	}

	return num;
}


/*
 * ファイルディスクリプターから該当ファイルを削除する。
 */
STATIC void delFromFd(F_DSC *fd)
{
	char path[VFS_MAX_NAME_SIZE];
	int len;


	/* ルートまでのパスを得る。 */
	enter_spinlock(&spinLock);
	{
		len = copyCurrentPath(path,VFS_MAX_NAME_SIZE,fd->vent);
		if (len >= VFS_MAX_NAME_SIZE)
			return;
	}
	exit_spinlock(&spinLock);

	/* 削除。 */
	rootEnt->fs->delete(path, rootEnt->din, rootEnt->inodeObj, S_IFREG);
}


//================================== PROTECTED ==========================================


/*
 * Get current directory entry.
 * return current directory entry
 */
STATIC INLINE VENTRY *getCurrentDir()
{
	return ((FILE_STRUCT*)get_current_task()->file_struct)->current_dir;
}


/*
 * Set current directory.
 */
STATIC INLINE void setCurrentDir(VENTRY *vent)
{
	FILE_STRUCT *fstruct;


	fstruct=get_current_task()->file_struct;
	unlinkVentry(fstruct->current_dir);
	fstruct->current_dir=vent;
}


/*
 * Get file descriptor flag.
 * parameters : file descriptor number,flag
 * return : flag or error number
 */
STATIC INLINE int getFdFlag(int num)
{
	FILE_STRUCT *fstruct;


	if(OPEN_MAX <= (uint)num)return -EBADF;
	fstruct=get_current_task()->file_struct;
	if(fstruct->fd[num]==NULL)return -EBADF;

	return fstruct->flag[num];
}


/*
 * Set file descriptor flag.
 * parameters : file descriptor number,flag
 * return : 0 or error number
 */
STATIC INLINE int setFdFlag(int num,int flag)
{
	FILE_STRUCT *fstruct;


	if(OPEN_MAX <= (uint)num)return -EBADF;
	fstruct=get_current_task()->file_struct;
	if(fstruct->fd[num]==NULL)return -EBADF;
	fstruct->flag[num]=flag;

	return 0;
}


/*
 * Get file open mode flag.
 * parameters : file descriptor number
 * returm : open mode flag
 */
STATIC INLINE int getOpenFlag(int num)
{
	FILE_STRUCT *fstruct;


	if (OPEN_MAX <= (uint)num){
		return -EBADF;
	}
	fstruct = get_current_task()->file_struct;
	if (fstruct->fd[num] == NULL){
		return -EBADF;
	}

	return fstruct->fd[num]->accessMode;
}


/*
 * Set file open mode flag.
 * parameters : file descriptor number
 * returm : open mode flag
 */
STATIC INLINE int setOpenFlag(int num,int flag)
{
	FILE_STRUCT *fstruct;


	if (OPEN_MAX <= (uint)num){
		return -EBADF;
	}
	fstruct = get_current_task()->file_struct;
	if (fstruct->fd[num] == NULL){
		return -EBADF;
	}

	fstruct->fd[num]->accessMode &= ~(O_APPEND | O_NONBLOCK);
	fstruct->fd[num]->accessMode |= flag & (O_APPEND | O_NONBLOCK);

	return 0;
}


/*
 * Copy file descriptor.
 * parameters : コピー元 FD number,コピー先最小 FD number
 * return : file descriptor number or error number
 */
STATIC int copyFd(int num1,int num2)
{
	FILE_STRUCT *fstruct = get_current_task()->file_struct;

	// 空きディスクリプター番号を得る
	if (OPEN_MAX <= (uint)num2){
		num2 = 0;
	}
	num2 = getEmptyFdNum(fstruct,num2);

	// ファイルディスクリプタをコピー
	fstruct->fd[num2] = fstruct->fd[num1];
	++fstruct->fd_count;
	++fstruct->fd[num2]->refCount;
	fstruct->flag[num2] = 0;			// Clear close-on-exec flag.
	linkVentry(fstruct->fd[num2]->vent);

	return num2;
}


/*
 * file descriptorの参照カウンタを減らす。
 * parameters : FILE_STRUCT,fd number
 * return : error number
 */
STATIC int unlinkFd(FILE_STRUCT *fstruct, int fdNum)
{
	int din;
	void *inodeObj;
	F_DSC *fd;
	FS *fs;
	int rest;

	fd = fstruct->fd[fdNum];
	--fd->refCount;
	if (fd->form == FD_FORM_NETSOKET){
		if (fd->refCount <= 0){
			closeSocket((SOCKET*)fd);
		}
	}
	else{
		fs = fd->vent->fs;
		din = fd->vent->din;
		inodeObj = fd->vent->inodeObj;
		if (unlinkVentry(fd->vent) == 0){
			rest = fs->close(din, inodeObj);
			WARNING(rest == 0);
		}

		if (fd->refCount <= 0){
			// 消去フラグを確認
			if (fd->accessMode & O_DEL){
				delFromFd(fd);
			}
			kfree(fd);
		}
	}

	fstruct->fd[fdNum] = NULL;
	fstruct->next_fd = fdNum;
	--fstruct->fd_count;

	return NOERR;
}


//================================== PUBLIC =============================================


/*
 * fork時にファイル関係の構造体を親プロセスからコピーする。
 * parameters : parent file structure
 * return : child file structure or error=NULL
 */
FILE_STRUCT *copyFileStruct(FILE_STRUCT *fstruct)
{
	FILE_STRUCT *cp_struct;
	int i,cnt;

	cp_struct = kmalloc(sizeof(FILE_STRUCT));
	if (cp_struct == NULL){
		return NULL;
	}
	memcpy(cp_struct, fstruct, sizeof(FILE_STRUCT));

	// ファイルディスクリプタの参照カウンタを1増やす
	for (cnt = fstruct->fd_count, i = 0; 0 < cnt; ++i){
		if (cp_struct->fd[i] != NULL){
			++cp_struct->fd[i]->refCount;
			if (cp_struct->fd[i]->form == FD_FORM_LOCALFILE){
				linkVentry(cp_struct->fd[i]->vent);
			}

			--cnt;
		}
	}

	return cp_struct;
}


/*
 * exit()時のファイル関係の構造体を開放する。
 * parameters : FILE_STRUCT address
 * return : 0 or error=-1
 */
void releaseFileStruct(FILE_STRUCT *fstruct)
{
	int i;

	for (i = 0; i < OPEN_MAX; ++i){
		if (fstruct->fd[i] != NULL){
			unlinkFd(fstruct, i);
			if (fstruct->fd_count <= 0){
				break;
			}
		}
	}

	kfree(fstruct);
}


/*
 * exec時のファイル構造体の初期化。
 */
void initExecFs()
{
	FILE_STRUCT *fstruct = get_current_task()->file_struct;
	int i, cnt;

	// Close file descriptor.
	for (cnt = fstruct->fd_count, i = 0; 0 < cnt; ++i){
		if (fstruct->fd[i] != NULL){
			if (fstruct->flag[i] & FD_CLOEXEC){
				unlinkFd(fstruct,i);
			}
			
			--cnt;
		}
	}
}


/*
 * ファイルディスクリプターからinodeを返す。
 * parameters : file descriptor
 * return : inode block or error=-1
 */
/*int get_inode(int fdNum)
{
	F_DSC *fd;


	if (OPEN_MAX <= fdNum){
		return -1;
	}
	if ((fd = ((FILE_STRUCT*)(get_current_task()->file_struct))->fd[fdNum]) == NULL){
		return -1;
	}

	return fd->vent->inodeObj;
}
*/

/*
 * アイドルプロセス用ファイル構造体の初期化。
 * parameters : process
 * return : 0 or error number
 */
int init_file_struct(PROC *proc)
{
	FILE_STRUCT *fstruct;


	if((fstruct=kmalloc(sizeof(FILE_STRUCT)))==NULL)return -ENOMEM;
	memset(fstruct,0,sizeof(FILE_STRUCT));
	fstruct->current_dir=rootEnt;
	proc->file_struct=fstruct;

	return 0;
}


// ファイルディスクリプタの初期化。
void initFd(VENTRY *vent, const int form, const int amode, F_DSC *m_fd)
{
	m_fd->form = form;
	m_fd->accessMode = amode;
	m_fd->f_pos = 0;
	m_fd->refCount = 1;
	m_fd->vent = vent;
}


/*
 * ファイルディスクリプタを登録する。
 * return : number of file descriptor
 */
int setFd(F_DSC *fd, const int flag)
{
	int num;
	FILE_STRUCT *fstruct = get_current_task()->file_struct;

	num = getEmptyFdNum(fstruct, fstruct->next_fd);
	fstruct->next_fd = (num + 1 < OPEN_MAX)? num + 1 : 0;
	++fstruct->fd_count;
	fstruct->fd[num] = fd;
	fstruct->flag[num] = flag;

	return num;
}


/*
 * File descriptorを取得する。
 * parameters : file descriptor number
 * return : file descriptor or NULL
 */
F_DSC *getFd(int num)
{
	if (OPEN_MAX <= (uint)num){
		return NULL;
	}
	return ((FILE_STRUCT*)get_current_task()->file_struct)->fd[num];
}


/*
 * オープンファイル数が最大か？
 * return : FALSE or TRUE
 */
int isFileOpenMax()
{
	FILE_STRUCT *fstruct = get_current_task()->file_struct;

	return  (OPEN_MAX <= fstruct->fd_count)? TRUE : FALSE;
}


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


//================================== PROTECTED ==========================================


/*
 * pathからトップディレクトリを得る。
 * parameters : パスアドレスポインタ
 * return : カレント仮想ディレクトリエントリー
 */
STATIC INLINE VENTRY *getTopVdir(const char *path,char **pPath)
{
	char *p = (char*)path;
	
	
	if(*p == '/')
	{
		while(*++p == '/');
		*pPath = p;
		return rootEnt;
	}
	else
	{
		*pPath = p;
		return getCurrentDir();
	}
}


/*
 * デバイスをオープンする。
 * return : error number
 */
STATIC int openDevice(const char *devPath, void **o_devInode)
{
	enum{DEV_NAME_SIZE = 5};		// "/dev/"のサイズ
	uint inodeNum;
	int type;
	int rest;

	// デバイスオープン
	if (cmpPathNull("/dev", devPath) == NULL){
		return -EINVAL;
	}
	rest = devEnt->mount->fs->lookup(devPath + DEV_NAME_SIZE, 0, 0, &inodeNum);
	if (rest < 0){
		return rest;
	}
	rest = devEnt->mount->fs->open(0, inodeNum, O_RDWR, o_devInode, &type);
	if (rest < 0){
		return rest;
	}

	return NOERR;
}


/*
 * 実行ファイルオープン。通常のオープンの簡略化関数。
 * return : 0 or error number
 */
int exec_open(const char *path, EXEC_FD *fd)
{
	uint inodeNum;
	int fileType;
	SEARCH_PARAM start, end;
/*	VENTRY *vent;*/
	int rest;

	if (*path == '\0'){
		return -ENOENT;
	}
	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}

	/* ファイルを探して、仮想エントリーを作成する。 */
	start.vent = getTopVdir(path,&start.path);
	if ((rest = getVentObj(&start,&end)) < 0){
		return rest;
	}

	if (*end.path == '\0'){
		if (end.vent->type != S_IFREG){
			rest = -EACCES;
			goto END;
		}
/*		linkVentry(end.vent);*/		/* 仮想ファイルへのリンクは保留。 */
		fd->din = end.vent->din;
		fd->inodeObj = end.vent->inodeObj;
		fd->fs = end.vent->fs;
	}
	else{
		void *inodeObj;

		rest = start.vent->fs->lookup(start.path, start.vent->din, start.vent->inodeObj, &inodeNum);
		if (rest < 0){
			goto END;
		}
		rest = start.vent->fs->open(start.vent->din, inodeNum, O_RDONLY, &inodeObj, &fileType);
		if (rest < 0){
			goto END;
		}
		if (fileType != S_IFREG){
			rest = -EACCES;
			goto END;
		}	
		fd->din = start.vent->din;
		fd->inodeObj = inodeObj;
		fd->fs = start.vent->fs;

		/* 仮想ファイルの作成は保留。 */
/*		enter_spinlock(&spinLock);
		{
			rest=mkEntry(end.path,end.vent);
		}
		exit_spinlock(&spinLock);

		if(rest<0)
		{
			start.vent->fs->close(start.vent->din,start.vent->inodeObj);
			goto END;
		}
*/
	}

	rest = NOERR;
END:
	releaseVentObj(end.vent);

	return rest;
}


/*
 * 実行ファイル読み込み。"sys_read"の簡略化関数。
 * parameters : open file struct,buffer,read size
 * return : read byte or error=-1
 */
int exec_read(EXEC_FD *fd, void *buf, size_t size)
{
	return fd->fs->read(fd->din, fd->inodeObj, buf, size, fd->offset);
}


/*
 * 実行ファイルのクローズ
 */
int exec_close(EXEC_FD *fd)
{
	return fd->fs->close(fd->din, fd->inodeObj);
}


int sys_mount(const char *devPath, const char *fsName, const char *path)
{
	void *mountRootObj;
	int mountDin;
	FS *fs;
	SEARCH_PARAM start, end;
	VENTRY *vent;
	int rest;

	CALL_PRINT();
	
	/* パス長の検査 */
	if (PATH_MAX <= strlen(devPath)){
		return -ENAMETOOLONG;
	}
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}

	/* Search file system. */
	fs = search_fs(fsName);
	if (fs == NULL){
		return -ENODEV;
	}

	/* デバイスオープン */
	rest = openDevice(devPath, (void**)&mountDin);
	if (rest < 0){
		return rest;
	}

	// マウント関数を呼び出す
	rest = fs->mount(mountDin, &mountRootObj);
	if (rest < 0){
		goto ERR;
	}

	// 仮想ファイルシステムのディレクトリを検索・作成する
	start.vent = getTopVdir(path,&start.path);
	rest = getVentObj(&start,&end);
	if (rest < 0){
		goto ERR;
	}
	if (*end.path != '\0'){
		uint inodeNum;
		void *rootInodeObj;
		int type;

		// ディレクトリが存在するか
		rest = start.vent->fs->lookup(start.path, start.vent->din, start.vent->inodeObj, &inodeNum);
		if (rest  < 0){
			goto ERR2;
		}

		// ファイルタイプの確認
		rest = start.vent->fs->open(start.vent->din, inodeNum, O_RDWR, &rootInodeObj, &type);
		if (rest  < 0){
			goto ERR2;
		}
		start.vent->fs->close(start.vent->din, rootInodeObj);
		if ((type & S_IFMT) != S_IFDIR){
			rest = -ENOTDIR;
			goto ERR2;
		}

		// 仮想ディレクトリの作成
		rest = mkEntry(end.path, end.vent);
		if (rest < 0){
			goto ERR2;
		}
		vent = (VENTRY*)rest;
		vent->type = type;
	}
	else{
		vent = end.vent;
	}

	/* マウントする仮想ディレクトリを作成する。 */
	rest = mountEntry(vent, fs, mountDin, mountRootObj);
	if (rest < 0){
		goto ERR2;
	}

	releaseVentObj(end.vent);

	return NOERR;

ERR2:
	releaseVentObj(end.vent);
ERR:
	fs->umount(mountDin, mountRootObj);

	return rest;
}


int sys_umount(const char *path)
{
	int rest;
	SEARCH_PARAM start, end;

	CALL_PRINT();

	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}

	/* ディレクトリを探す。 */
	start.vent = getTopVdir(path,&start.path);
	rest = getVentObj(&start,&end);
	if (rest < 0){
		return rest;
	}
	if ((*end.path != '\0') || (end.vent->type != S_IFDIR))
	{
		rest=-ENOENT;
		goto END;
	}

	/* アンマウント。 */
	rest = umountEntry(end.vent);
END:
	releaseVentObj(end.vent);

	return rest;
}


int sys_lseek(int fdNum, int offset, int whence)
{
	int rest;
	struct stat fstat;
	F_DSC *fd;
	VENTRY *vent;

	CALL_PRINT();

	fd = getFd(fdNum);
	if (fd == NULL){
		return -EBADF;
	}

	vent = fd->vent;
	vent->fs->stat(vent->din, vent->inodeObj, &fstat);
	switch(whence){
		case SEEK_CUR:
			rest = fd->f_pos + offset;
			break;
		case SEEK_SET:
			rest = offset;
			break;
		case SEEK_END:
			rest = fstat.st_size - offset;
			break;
		default:
			return -EINVAL;
	}
	if (rest < 0){
		return -EINVAL;
	}

	return fd->f_pos = rest;
}


//《検討》FIFOファイルでのオープン処理
int sys_creat(const char *path, int flag, int mode)
{
	uint inodeNum;
	void *inodeObj;
	int type;
	SEARCH_PARAM start,end;
	VENTRY *vent;
	F_DSC *fd;
	int rest;

	CALL_PRINT();

	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}
	if(*path=='\0'){
		return -ENOENT;
	}
	/* オープン数のチェック。 */
	if (isFileOpenMax() == TRUE){
		return -EMFILE;
	}

	/* ファイルを探して、仮想エントリーを作成する。 */
	start.vent = getTopVdir(path, &start.path);
	if ((rest = getVentObj(&start, &end)) < 0){
		return rest;
	}

	if (*end.path == '\0'){
		rest = -EEXIST;
		goto ERR;
	}

	// ファイルの作成
	if (flag & O_FIFO){
		mode |= S_IFIFO;
	}
	else{
		mode |=  S_IFREG | S_IRUSR | S_IWUSR;
	}
	rest = start.vent->fs->creat(start.path, start.vent->din, start.vent->inodeObj, mode, &inodeNum);
	if (rest < 0){
		goto ERR;
	}

	// 仮想ファイルの作成
	enter_spinlock(&spinLock);
	{
		rest = mkEntry(end.path,end.vent);
	}
	exit_spinlock(&spinLock);
	if (rest < 0){
		goto ERR;
	}
	vent = (VENTRY*)rest;

	// ファイルディスクリプタの初期化
	fd = kmalloc(sizeof(F_DSC));
 	if (fd == NULL){
		rest = -ENOMEM;
		goto ERR2;
	}
	initFd(vent, FD_FORM_LOCALFILE, flag, fd);

	// ファイルオープン
	rest = start.vent->fs->open(vent->din, inodeNum, flag, &inodeObj, &type);
	if (rest < 0){
		goto ERR3;
	}
	vent->inodeObj = inodeObj;
	vent->type = type;

	// FIFOファイル
	if (flag & O_FIFO){
		vent->wait = kmalloc(sizeof(WAIT_QUEUE));
		if (vent->wait == NULL){
			rest = -ENOMEM;
			goto ERR4;
		}
		memset(vent->wait, 0, sizeof(WAIT_QUEUE));
		vent->type=S_IFIFO;
	}

	// ファイルディスクリプタの登録
	rest = setFd(fd, FD_CLOEXEC);

	releaseVentObj(end.vent);

	return rest;

ERR4:
	vent->fs->close(vent->din, vent->inodeObj);
ERR3:
	kfree(fd);
ERR2:
	unlinkVentry(vent);
ERR:
	releaseVentObj(end.vent);
	return rest;
}


//《検討》FIFOファイルでのオープン処理
// return : file descriptor number or error number
int sys_open(const char *path, int flag, int mode)
{
	uint inodeNum = UINT_MAX;	//UINT_MAXならlookup()で設定されていないのでopen()しない。
	void *inodeObj;
	int type;
	SEARCH_PARAM start, end;
	VENTRY *vent;
	F_DSC *fd;
	int fdNum;
	int rest;

	CALL_PRINT();

	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}
	if(*path=='\0'){
		return -ENOENT;
	}
	if ((flag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)){
		return sys_creat(path, flag, mode);
	}
	/* オープン数のチェック。 */
	if (isFileOpenMax() == TRUE){
		return -EMFILE;
	}

	// 仮想エントリーを探す
	start.vent = getTopVdir(path, &start.path);
	rest = getVentObj(&start, &end);
	if (rest < 0){
		return rest;
	}

	// 仮想ファイルにあり
	if (*end.path == '\0'){
		if (end.vent->type == S_IFDIR){
			if (end.vent->refCount == 0){
				rest = start.vent->fs->lookup(start.path, start.vent->din, start.vent->inodeObj, &inodeNum);
				if (rest < 0){
					goto ERR;
				}
			}
		}

		// 仮想ファイルの参照数を増やす
		vent = end.vent;
		linkVentry(vent);
	}
	else{
		rest = start.vent->fs->lookup(start.path, start.vent->din, start.vent->inodeObj, &inodeNum);
		if ((rest == -ENOENT) && (flag & O_CREAT)){
			if (flag & O_FIFO){
				mode |= S_IFIFO;
			}
			else{
				mode |=  S_IFREG | S_IRUSR | S_IWUSR;
			}
			rest = start.vent->fs->creat(start.path, start.vent->din, start.vent->inodeObj, mode, &inodeNum);
		}
		if (rest < 0){
			goto ERR;
		}

		// 仮想ファイルの作成
		enter_spinlock(&spinLock);
		{
			rest = mkEntry(end.path, end.vent);
		}
		exit_spinlock(&spinLock);
		if (rest < 0){
			goto ERR;
		}
		vent = (VENTRY*)rest;
	}

	// ファイルディスクリプタの初期化
	fd = kmalloc(sizeof(F_DSC));
 	if (fd == NULL){
		rest = -ENOMEM;
		goto ERR2;
	}
	initFd(vent, FD_FORM_LOCALFILE, flag, fd);
	
	// ファイルオープン
	if (inodeNum != UINT_MAX){
		rest = start.vent->fs->open(vent->din, inodeNum, flag, &inodeObj, &type);
		if (rest < 0){
			goto ERR3;
		}
		vent->inodeObj = inodeObj;
		vent->type = type;
	}

	/* ディレクトリは読み込みのみ */
	if (vent->type == S_IFDIR){
		if (flag != O_RDONLY){
			rest = -EISDIR;
			goto ERR4;
		}
	}
	// TRUNCATEでオープン
	if (flag & O_TRUNC){
		if ((vent->type & S_IFMT) == S_IFREG){
			rest = vent->fs->ioctl(vent->din, vent->inodeObj, I_TRUNC, NULL, 0);
			if (rest < 0){
				goto ERR4;
			}
		}
	}
	// FIFOファイルでのオープン
	if (flag & O_FIFO){
		vent->wait = kmalloc(sizeof(WAIT_QUEUE));
		if (vent->wait == NULL){
			rest = -ENOMEM;
			goto ERR4;
		}
		memset(vent->wait, 0, sizeof(WAIT_QUEUE));
		vent->type = S_IFIFO;
	}

	// ファイルディスクリプタの登録
	fdNum = setFd(fd, FD_CLOEXEC);

	// APPENDでオープン
	if (flag & O_APPEND){
		sys_lseek(fdNum, 0, SEEK_END);
	}

	releaseVentObj(end.vent);

	return fdNum;

ERR4:
	vent->fs->close(vent->din, vent->inodeObj);
ERR3:
	kfree(fd);
ERR2:
	unlinkVentry(vent);
ERR:
	releaseVentObj(end.vent);
	return rest;
}


int sys_close(int fdNum)
{
	F_DSC *fd;
	FILE_STRUCT *fstruct;

	CALL_PRINT();

	if ((fd = getFd(fdNum)) == NULL){
		return -EBADF;
	}
	fstruct = (FILE_STRUCT*)get_current_task()->file_struct;

	return unlinkFd(fstruct, fdNum);
}


/*
 * parameters : file descriptor number,baffer,read bytes
 * return : read bytes or error=-1
 */
int sys_read(int fdNum, void *buf, size_t size)
{
	F_DSC *fd;
	VENTRY *vent;
	int rest;

	CALL_PRINT();

	/* バッファの妥当性チェック */
	if (checkMem(buf, size) == ERR){
		return -EFAULT;
	}

	fd = getFd(fdNum);
	if (fd == NULL){
		return -EBADF;
	}

	/* アクセスモードのチェック */
	if ((fd->accessMode & (O_RDONLY | O_RDWR)) == 0){
		return -EBADF;
	}

	vent = fd->vent;
	if (vent->type == S_IFIFO){
		waitSemaphore(vent->wait);
		{
			rest = vent->fs->read(vent->din, vent->inodeObj, buf, size, 0);
		}
		wakeSemaphore(vent->wait);
	}
	else{
		rest = vent->fs->read(vent->din, vent->inodeObj, buf, size, fd->f_pos);
		if (0 < rest){
			fd->f_pos += rest;
		}
	}

	return rest;
}


int sys_write(int fdNum, void *buf, size_t size)
{
	F_DSC *fd;
	VENTRY *vent;
	int rest;

	CALL_PRINT();

	/* バッファの妥当性チェック */
	if (checkMem(buf, size) == -1){
		return -EFAULT;
	}

	fd = getFd(fdNum);
	if (fd == NULL){
		return -EBADF;
	}

	/* アクセスモードのチェック。 */
	if ((fd->accessMode & (O_WRONLY | O_RDWR)) == 0){
		return -EBADF;
	}

	/* write関数を呼び出す。 */
	vent = fd->vent;
	if (vent->type == S_IFIFO){
		waitSemaphore(vent->wait);
		{
			rest=vent->fs->write(vent->din, vent->inodeObj, buf, size, 0);
		}
		wakeSemaphore(vent->wait);
	}
	else{
		rest = vent->fs->write(vent->din, vent->inodeObj, buf, size, fd->f_pos);
		if (0 < rest){
			fd->f_pos += rest;
		}
	}

	return rest;
}


int sys_ioctl(int fdNum, int cmd, caddr_t parm, int fflag)
{
	F_DSC *fd;
	VENTRY *vent;

	CALL_PRINT();

	if ((fd = getFd(fdNum)) == NULL){
		return -EBADF;
	}
	vent = fd->vent;

	return vent->fs->ioctl(vent->din, vent->inodeObj, cmd, parm, fflag);
}


int sys_mkdir(const char *path, int mode)
{
	SEARCH_PARAM start,end;
	int rest;

	CALL_PRINT();

	if (*path == '\0'){
		return -ENOENT;
	}
	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}

	start.vent = getTopVdir(path,&start.path);
	rest = getVentObj(&start, &end);
	if (rest < 0){
		return rest;
	}
	if (*end.path == '\0'){
		rest = -EEXIST;
		goto END;
	}
	rest = start.vent->fs->mkdir(start.path, start.vent->din, start.vent->inodeObj, mode);
END:
	releaseVentObj(end.vent);

	return rest;
}


int sys_unlink(const char *path)
{
	SEARCH_PARAM start,end;
	int rest;

	CALL_PRINT();

	if (*path == '\0')
		return -ENOENT;
	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}

	start.vent = getTopVdir(path,&start.path);
	if ((rest = getVentObj(&start,&end)) < 0){
		return rest;
	}
	if (*end.path != '\0'){
		rest = start.vent->fs->delete(start.path, start.vent->din, start.vent->inodeObj, S_IFREG);
	}
	else {
		unlinkVentryLink(end.vent);
		rest = 0;
	}

	releaseVentObj(end.vent);

	return rest;
}


int sys_rmdir(const char *path)
{
	SEARCH_PARAM start,end;
	int rest;

	CALL_PRINT();

	if (*path == '\0'){
		return -ENOENT;
	}
	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}

	start.vent = getTopVdir(path,&start.path);
	if((rest=getVentObj(&start,&end))<0){
		return rest;
	}
	if(*end.path!='\0'){
		rest=start.vent->fs->delete(start.path, start.vent->din, start.vent->inodeObj, S_IFDIR);
	}
	else{
		rest=-EBUSY;
	}

	releaseVentObj(end.vent);

	return rest;
}


int sys_opendir(const char *path)
{
	uint block;
	void *inodeObj;
	int index;
	SEARCH_PARAM start, end;
	VENTRY *vent;
	DIR_STRAEM *dir;
	int rest;

	CALL_PRINT();

	if (*path == '\0'){
		return -ENOENT;
	}
	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}

	/* ファイルを探す。 */
	start.vent = getTopVdir(path, &start.path);
	rest = getVentObj(&start, &end);
	if (rest < 0){
		return rest;
	}
	if(*end.path == '\0'){
		if(end.vent->type != S_IFDIR){
			rest = -ENOTDIR;
			goto END;
		}
		linkVentry(end.vent);
		vent = end.vent;
		rest = start.vent->fs->opendir(end.path, vent->din, vent->inodeObj, &inodeObj, &block, &index);
		if (rest < 0){
			goto END;
		}
	}
	else{
		if(end.vent->type == S_IFDIR){
			rest = start.vent->fs->opendir(start.path, start.vent->din, start.vent->inodeObj, &inodeObj, &block, &index);
			if (rest < 0){
				goto END;
			}

			/* 仮想エントリーの作成。 */
			enter_spinlock(&spinLock);
			{
				rest = mkEntry(end.path, end.vent);
			}
			exit_spinlock(&spinLock);
			if(rest < 0){
				start.vent->fs->close(start. vent->din, start.vent->inodeObj);
				goto END;
			}
			vent = (VENTRY*)rest;
			vent->inodeObj = inodeObj;
			vent->type = S_IFDIR;
		}
		else{
			rest = -ENOENT;
			goto END;
		}
	}

	/* ディレクトリストリームの作成。 */
	dir = kmalloc(sizeof(DIR_STRAEM));
	if (dir == NULL){
		rest=-ENOMEM;
		goto END;
	}
	initFd(vent, FD_FORM_DIRSTREAM, 0, (F_DSC*)dir);
	rest = setFd((F_DSC*)dir, FD_CLOEXEC);
	dir->block = block;
	dir->index = index;
END:
	releaseVentObj(end.vent);

	return rest;
}


int sys_readdir(int dirNum, char *name)
{
	DIR_STRAEM *dir;
	VENTRY *vent;

	CALL_PRINT();

	dir = (DIR_STRAEM*)getFd(dirNum);
	if (dir == NULL){
		return -EBADF;
	}
	if (dir->fd.form != FD_FORM_DIRSTREAM){
		return -EBADF;
	}

	vent = dir->fd.vent;

	return vent->fs->readdir(vent->din, vent->inodeObj, &dir->block, &dir->index, name);
}


int sys_rewinddir(int dirNum)
{
	DIR_STRAEM *dir;

	CALL_PRINT();

	dir = (DIR_STRAEM*)getFd(dirNum);
	if(dir == NULL){
		return -EBADF;
	}
	if (dir->fd.form != FD_FORM_DIRSTREAM){
		return -EBADF;
	}
	dir->index = 0;

	return 0;
}


int sys_rename(const char *old_path,const char *new_path)
{
	SEARCH_PARAM old_start, old_end;
	SEARCH_PARAM new_start, new_end;
	int rest;

	CALL_PRINT();

	if (strcmp(old_path, new_path) == 0){
		return 0;
	}

	/* 元ファイルを探す。 */
	old_start.vent = getTopVdir(old_path, &old_start.path);
	rest = getVentObj(&old_start,&old_end);
	if(rest < 0){
		return rest;
	}

	/* 新ファイルを探す。 */
	new_start.vent = getTopVdir(new_path, &new_start.path);
	rest = getVentObj(&new_start, &new_end);
	if (rest < 0){
		goto END;
	}

	if (old_start.vent->din == new_start.vent->din){
		if ((*old_end.path != '\0') && (*new_end.path != '\0')){
			rest = old_start.vent->fs->
				rename(old_start.vent->din, old_start.vent->inodeObj, old_start.path, new_start.vent->inodeObj, new_start.path);
		}
		else{
			rest = -EBUSY;
		}
	}
	else{
		rest = -EXDEV;
	}

	releaseVentObj(new_end.vent);
END:
	releaseVentObj(old_end.vent);

	return rest;
}


int sys_chdir(const char *path)
{
	void *inodeObj;
	int rest = 0;
	SEARCH_PARAM start,end;
	VENTRY *vent;
	int dummy1;
	uint dummy2;

	
	CALL_PRINT();

	if (*path == '\0'){
		return -ENOENT;
	}
	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}

	/* ディレクトリを探す。 */
	start.vent = getTopVdir(path,&start.path);
	rest = getVentObj(&start,&end);
	if (rest < 0){
		return rest;
	}
	if (end.vent->type == S_IFDIR){
		if (*end.path == '\0'){
			if (end.vent->refCount == 1){
				rest = start.vent->fs->opendir(start.path, start.vent->din, start.vent->inodeObj, &inodeObj, &dummy1, &dummy2);
				if (rest < 0){
					goto ERR;
				}
				end.vent->inodeObj = inodeObj;
			}
			linkVentry(end.vent);
			vent = end.vent;
		}
		else{
			rest = start.vent->fs->opendir(start.path,start.vent->din,start.vent->inodeObj,&inodeObj,&dummy1,&dummy2);
			if (rest >= 0){
				enter_spinlock(&spinLock);
				{
					rest = mkEntry(end.path,end.vent);
				}
				exit_spinlock(&spinLock);

				if (rest < 0){
					start.vent->fs->close(start.vent->din,start.vent->inodeObj);
					goto ERR;
				}
				vent = (VENTRY*)rest;
				vent->inodeObj = inodeObj;
				vent->type = S_IFDIR;
			}
			else{
				goto ERR;
			}
		}
	}
	else{
		rest = -ENOTDIR;
		goto ERR;
	}

	releaseVentObj(end.vent);

	/* ファイル構造体に新しいカレントディレクトリを登録。 */
	setCurrentDir(vent);

	return 0;
ERR:
	releaseVentObj(end.vent);

	return rest;
}


int sys_fchdir(int fdNum)
{
	F_DSC *fd;


	CALL_PRINT();

	fd = getFd(fdNum);
	if (fd == NULL){
		return -EBADF;
	}
	if (fd->vent->type != S_IFDIR){
		return -ENOTDIR;
	}

	linkVentry(fd->vent);
	
	/* ファイル構造体に新しいカレントディレクトリを登録。 */
	setCurrentDir(fd->vent);

	return 0;
}


int sys_getcwd(char *buf, size_t size)
{
	int len;
	VENTRY *vent;

	CALL_PRINT();

	if (size == 0){
		return -EINVAL;
	}

	/* バッファの妥当性チェック */
	if (checkMem(buf, size) == ERR){
		return -EFAULT;
	}

	vent = getCurrentDir();
	if ((len = copyCurrentPath(buf, size, vent)) == -1){
		return -ERANGE;
	}
	if (len == 1){
		buf[1] = '\0';
	}
	else{
		buf[len-1] = '\0';
	}

	return 0;
}


/*
 * 引数のpathがNULLなら引数のfile descriptorから探す。
 * parameters : file descriptor,path,stat buffer
 */
int sys_stat(int fdNum, const char *path, struct stat *m_stat)
{
	F_DSC *fd;
	SEARCH_PARAM start, end;
	int rest;

	CALL_PRINT();

	if (checkMem(m_stat,sizeof(struct stat)) == ERR){
		return -EFAULT;
	}
	if (path == NULL){
		if ((fd = getFd(fdNum)) == NULL){
			return -EBADF;
		}
		m_stat->st_crtpos = fd->f_pos;

		return fd->vent->fs->stat(fd->vent->din, fd->vent->inodeObj, m_stat);
	}
	else{
		/* パス長の検査 */
		if (PATH_MAX <= strlen(path)){
			return -ENAMETOOLONG;
		}
		m_stat->st_crtpos = 0;

		start.vent = getTopVdir(path, &start.path);
		rest = getVentObj(&start, &end);
		if (rest < 0){
			return rest;
		}
		if ((*end.path == '\0') && (end.vent->inodeObj != 0)){
			rest = end.vent->fs->stat(end.vent->din, end.vent->inodeObj, m_stat);
		}
		else{
			uint inodeNum;
			void *inodeObj;
			int type;

			rest = start.vent->fs->lookup(start.path, start.vent->din, start.vent->inodeObj, &inodeNum);
			if (rest < 0){
				goto END;
			}
			rest = start.vent->fs->open(start.vent->din, inodeNum, O_RDONLY, &inodeObj ,&type);
			if (rest < 0){
				goto END;
			}
			rest = start.vent->fs->stat(start.vent->din, inodeObj, m_stat);
			start.vent->fs->close(start.vent->din, inodeObj);
		}
	}
END:
	releaseVentObj(end.vent);

/************** 未定義 **************************/
	m_stat->st_flags = 0;
/*****************************************************/

	return rest;
}


/*
 * PRIVATE
 * 仮想ファイルシステムを検索してマウントされているファイルシステムをアンマウントする。
 */
STATIC void umountAllFs(VENTRY *ventry)
{
	VENTRY *vent = ventry;

	do{
		if (vent->child != NULL){
			umountAllFs(vent->child);
		}
		/* umouont file system. */
		if (vent->mount != NULL){
			vent->mount->fs->umount(vent->mount->din, vent->mount->inodeObj);
		}
	}while ((vent = vent->next) != ventry);
}


/*
 * ルートファイルシステムをアンマウントする。
 */
int sys_umount_root()
{
	CALL_PRINT();

	umountAllFs(rootEnt);
	rootEnt->fs->umount(rootEnt->din, rootEnt->inodeObj);

	return 0;
}


int sys_fcntl(int fdNum, int cmd, uint arg)
{
	CALL_PRINT();

	switch(cmd){
		case F_DUPFD:{
			if (isFileOpenMax() == TRUE){
				return -EMFILE;
			}
			if (getFd(fdNum) == NULL){			/* File descriptorのチェック。 */
				return -EBADF;
			}
			return copyFd(fdNum,arg);			/* Copy File descriptor. */
		}
		case F_GETFD:
			return getFdFlag(fdNum);
		case F_SETFD:
			return setFdFlag(fdNum,arg);
		case F_GETFL:
			return getOpenFlag(fdNum);
		case F_SETFL:
			return setOpenFlag(fdNum, arg);
		case F_GETLK:
		case F_SETLK:
		case F_SETLKW:
		case F_GETOWN:
		case F_SETOWN:
		default:
			return -ENOTSUP;
	}
}


STATIC int chattr(FS *fs, int din, void *inodeObj, int cmd, uint attr)
{
	int rest;
	PROC *proc;
	struct stat stat;

	/* Change attribute. */
	proc=get_current_task();
	if ((rest = fs->stat(din, inodeObj, &stat)) < 0){
		return rest;
	}
	switch(cmd){
		case CHATTR_MODE:
			if ((proc->uid == 0) || (proc->uid == stat.st_uid)){
				rest = fs->chattr(din, inodeObj, attr, stat.st_uid, stat.st_gid, stat.st_atime, stat.st_mtime);
			}
			else{
				rest=-EPERM;
			}
			break;
		case CHATTR_UID:
			if(proc->uid == 0){
				rest = fs->chattr(din, inodeObj, stat.st_mode, attr, stat.st_gid, stat.st_atime, stat.st_mtime);
			}
			else{
				rest=-EPERM;
			}
			break;
		case CHATTR_GID:
			if (((proc->uid == 0) || (proc->uid == stat.st_uid)) && (attr == proc->gid)){
				rest = fs->chattr(din, inodeObj, stat.st_mode, stat.st_gid, attr, stat.st_atime, stat.st_mtime);
			}
			else{
				rest=-EPERM;
			}
			break;
		case CHATTR_UTIME:
			if ((proc->uid == 0) || (proc->uid == stat.st_uid)){
				struct utimbuf *ut = (struct utimbuf*)attr;
				uint time;

				if (ut == NULL){
					time = sys_time(NULL);
					rest = fs->chattr(din, inodeObj, stat.st_mode, stat.st_gid, attr, time, time);
				}
				else{
					rest = fs->chattr(din, inodeObj, stat.st_mode, stat.st_gid, attr, ut->actime, ut->modtime);
				}
			}
			else{
				rest=-EPERM;
			}
			break;
	}

	return rest;
}


int sys_chattr(const char *path, int cmd, uint attr)
{
	SEARCH_PARAM start,end;
	int rest;

	CALL_PRINT();

	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}

	/* Open inode. */
	start.vent = getTopVdir(path, &start.path);
	rest = getVentObj(&start, &end);
	if (rest < 0){
		return rest;
	}

	if ((*end.path == '\0') && (end.vent->inodeObj != 0)){
		rest = chattr(end.vent->fs, end.vent->din, end.vent->inodeObj, cmd, attr);
	}
	else{
		uint inodeNum;
		void *inodeObj;
		int type;

		rest = start.vent->fs->lookup(start.path, start.vent->din, start.vent->inodeObj, &inodeNum);
		if (rest < 0){
			goto ERR;
		}
		rest = start.vent->fs->open(start.vent->din, inodeNum, O_RDWR, &inodeObj, &type);
		if (rest < 0){
			goto ERR;
		}
		rest = chattr(start.vent->fs, start.vent->din, inodeObj, cmd, attr);
		start.vent->fs->close(start.vent->din, inodeObj);
	}
ERR:
	releaseVentObj(end.vent);

	return rest;
}


int sys_fchattr(int fdNum, int cmd, uint attr)
{
	F_DSC *fd;
	int rest;

	CALL_PRINT();

	if ((fd = getFd(fdNum)) == NULL){
		return -EBADF;
	}
	if (fd->vent->type != S_IFREG){
		return -EBADF;
	}

	setVentObj(fd->vent);
	rest = chattr(fd->vent->fs, fd->vent->din, fd->vent->inodeObj, cmd, attr);
	releaseVentObj(fd->vent);

	return rest;
}


int sys_link(const char *dst_path, const char *src_path)
{
	void *dstInodeObj;
	int din;
	int rest;
	SEARCH_PARAM dst_start, src_start, dst_end, src_end;

	CALL_PRINT();

	/* パス長の検査 */
	if (PATH_MAX <= strlen(dst_path)){
		return -ENAMETOOLONG;
	}
	if (PATH_MAX <= strlen(src_path)){
		return -ENAMETOOLONG;
	}

	/* dst_pathのファイルを開く。 */
	dst_start.vent = getTopVdir(dst_path,&dst_start.path);
	rest = getVentObj(&dst_start,&dst_end);
	if (rest < 0){
		return rest;
	}
	if (*dst_end.path == '\0'){
		if (dst_end.vent->type == S_IFDIR){
			rest = -EISDIR;
			goto END;
		}
		din = dst_end.vent->din;
		dstInodeObj = dst_end.vent->inodeObj;
	}
	else{
		uint inodeNum;
		void *inodeObj;
		int type;

		din = dst_start.vent->din;
		rest = dst_start.vent->fs->lookup(dst_start.path, din, dst_start.vent->inodeObj, &inodeNum);
		if (rest < 0){
			goto END;
		}
		rest = dst_start.vent->fs->open(din, inodeNum, O_RDONLY, &inodeObj, &type);
		if (rest < 0){
			goto END;
		}
		dstInodeObj = inodeObj;
	}

	/* src_pathのファイルを検索する。 */
	src_start.vent = getTopVdir(src_path,&src_start.path);
	if ((rest = getVentObj(&src_start,&src_end)) < 0){
		return rest;
	}
	if (*src_end.path == '\0'){
		rest = -EEXIST;
	}
	else{
		if (src_start.vent->din != din){
			rest=-EXDEV;
		}
		else{
			rest = src_start.vent->fs->link(din, dstInodeObj, src_start.vent->inodeObj, src_start.path);
		}
	}
	releaseVentObj(src_end.vent);

	if (*dst_end.path != '\0'){
		dst_start.vent->fs->close(din, dstInodeObj);
	}
END:
	releaseVentObj(dst_end.vent);

	return rest;
}


int sys_symlink(char *linkPath, const char *filePath)
{
	SEARCH_PARAM start, end;
	int rest;
	
	
	CALL_PRINT();

	/* パス長の検査 */
	if (PATH_MAX <= strlen(filePath)){
		return -ENAMETOOLONG;
	}
	if (PATH_MAX <= strlen(linkPath)){
		return -ENAMETOOLONG;
	}

	if (*filePath == '\0'){
		return -ENOENT;
	}

	/* filePathを開く。 */
	start.vent = getTopVdir(filePath,&start.path);
	if ((rest = getVentObj(&start,&end)) < 0){
		return rest;
	}
	if (*end.path == '\0'){
		rest = -EEXIST;
		goto END;
	}
	else{
		rest = start.vent->fs->symlink(start.path, start.vent->din, start.vent->inodeObj, linkPath);
		if (rest < 0){
			goto END;
		}
	}
	
	rest = 0;
END:
	releaseVentObj(end.vent);

	return rest;
}


int sys_readlink(const char *path, char *buf, size_t bufSize)
{
	void *inodeObj;
	SEARCH_PARAM start, end;
	struct stat stat;
	int size;
	int rest = 0;

	CALL_PRINT();

	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}
	/* ポインタ妥当性の確認 */
	if (checkMem(buf,bufSize) == ERR){
		return -EFAULT;
	}

	/* ファイルを検索する */
	start.vent = getTopVdir(path,&start.path);
	if ((rest = getVentObj(&start,&end)) < 0){
		return rest;
	}
	/* inodeを取得する */
	if (*end.path == '\0'){
		inodeObj = end.vent->inodeObj;
	}
	else{
		uint inodeNum;
		int type;

		rest = start.vent->fs->lookup(start.path, start.vent->din, start.vent->inodeObj, &inodeNum);
		if (rest < 0){
			goto END;
		}
		rest = start.vent->fs->open(start.vent->din, inodeNum, O_RDONLY, &inodeObj, &type);
		if (rest < 0){
			goto END;
		}
	}

	/* ファイルタイプを確認する */
	rest = start.vent->fs->stat(start.vent->din, inodeObj, &stat);
	if (rest < 0){
		goto END2;
	}
	if ((stat.st_mode & S_IFLNK) == 0){
		rest = -EINVAL;
		goto END2;
	}
	
	/* リンクパスを読む */
	size = (stat.st_size <= bufSize)? stat.st_size : bufSize;
	rest = start.vent->fs->read(start.vent->din, inodeObj, buf, size, 0);

END2:	
	if (*end.path != '\0'){
		start.vent->fs->close(start.vent->din, inodeObj);
	}
END:
	releaseVentObj(end.vent);

	return rest;
}


// fd_set構造体同士のOR演算
STATIC INLINE void orFdset(const int lastFdNum, fd_set *dstFdSet, fd_set *srcFdSet)
{
	int last = (lastFdNum < FD_SETSIZE)? lastFdNum / CHAR_BIT + 1 : FD_BITMAP_LEN;
	int i;
	
	for (i = 0; i < last; ++i){
		dstFdSet->bitmap[i] |= srcFdSet->bitmap[i];
	}
}

// fd_set構造体から引数のディスクリプター番号以上の設定されているディスクリプター番号を返す。
// return : number of discriptor or -1 = なし
STATIC INLINE int getFdFromFdset(const int lastFdNum, const int firstFd, const fd_set *fdSet)
{
	int fdNum;
	int array;
	int last = (lastFdNum < FD_SETSIZE)? lastFdNum / CHAR_BIT + 1 : FD_BITMAP_LEN;
	
	for (array = firstFd / CHAR_BIT, fdNum = firstFd % CHAR_BIT; array < last; ++array){
		if (fdSet->bitmap[array] != 0){
			for (; fdNum < CHAR_BIT; ++fdNum){
				if (fdSet->bitmap[array] & (1 << fdNum)){
					return array * CHAR_BIT + fdNum;
				}
			}
		}
		fdNum = 0;
	}
	return -1;
}


int sys_select(const int lastFdNum, fd_set *m_fdsRead, fd_set *m_fdsWrite, fd_set *m_fdsError, struct timeval *timeout)
{
	enum{
		FDS_LAST = 3,
	};
	int seconds;
	fd_set fdSetAll;
	fd_set *fdSet[] = {m_fdsRead, m_fdsWrite, m_fdsError};
	int pollEvents[] = {POLLIN, POLLOUT, POLLERR};
	int isFds;
	int rest;
	int i;

	CALL_PRINT();

	if (lastFdNum < 0){
		return -EINVAL;
	}

	// タイムアウト時間の設定
	if (timeout == NULL){
		// シグナル発生までブロックする
		seconds = -1;
	}
	else{
		// １秒未満四捨五入
		seconds = timeout->tv_sec + (500000 <= timeout->tv_usec);
	}

	// 全FDの抽出
	FD_ZERO(&fdSetAll);
	isFds = NO;
	for (i = 0; i < FDS_LAST; ++i){
		if (fdSet[i] != NULL){
			if (checkMem(fdSet[i], sizeof(fd_set)) == ERR){
				return -EFAULT;
			}
			isFds = YES;
			orFdset(lastFdNum, &fdSetAll, fdSet[i]);
		}
	}

	if (isFds == YES){
		// イベントの検索
		for (;;){
			int fdNum;

			for (fdNum = 0; (fdNum = getFdFromFdset(lastFdNum, fdNum, &fdSetAll)) != -1;){
				int c_update = 0;
				int events;
				F_DSC *fd = getFd(fdNum);

				if (fd == NULL){
					return -EBADF;
				}
				// 引数に渡すイベントの設定
				events = 0;
				for (i = 0; i < FDS_LAST; ++i){
					if (FD_ISSET(fdNum, fdSet[i])){
						events |= pollEvents[i];
					}
				}
				rest = fd->vent->fs->poll(fd->vent->inodeObj, events);
				
				// アクセス可能FDのカウント
				for (i = 0; i < FDS_LAST; ++i){
					if (rest & pollEvents[i]){
						if (fdSet[i] != NULL){
							if (FD_ISSET(fdNum, fdSet[i])){
								c_update += 1;
							}
						}
					}
				}
				// FD_SETにイベントを設定
				if (0 < c_update){
					for (i = 0; i < FDS_LAST; ++i){
						if (rest & pollEvents[i]){
							if (fdSet[i] != NULL){
								if (FD_ISSET(fdNum, fdSet[i])){
									FD_ZERO(fdSet[i]);
									FD_SET(fdNum, fdSet[i]);
								}
								else{
									FD_ZERO(fdSet[i]);
								}
							}
						}
					}
					return c_update;
				}
				fdNum += 1;
			}
			
			if (seconds == 0){
				// 更新ディスクリプタ無し
				for (i = 0; i < FDS_LAST; ++i){
					if (fdSet[i] != NULL){
						FD_ZERO(fdSet[i]);
					}
				}
				return 0;
			}
			
			// 待ち
			sys_sleep(seconds);

			// シグナル割り込みが起きたか？
			doSignalRestart();
			if(isSigint() == TRUE){
				return -EINTR;
			}

			// ２回目は待ち時間をクリアー
			seconds = 0;
		}
	}
	else{
		// シグナル待ち。
		sys_sleep(seconds);

		return 0;
	}
}


int sys_poll(struct pollfd *ufds, const nfds_t nfds, const int miliSeconds)
{
	int seconds;
	int i, rest;

	CALL_PRINT();

	if (RLIMIT_NOFILE < nfds){
		return -EINVAL;
	}

	// メモリーチェック
	if (checkMem(ufds, sizeof(ufds) * nfds) == -1){
		return -EFAULT;
	}
	for (i = 0; i < nfds; ++i){
		if (checkMem(&ufds[i], sizeof(struct pollfd)) == ERR){
			return -EFAULT;
		}
	}
	
	// タイムアウト時間の設定
	if (miliSeconds < 0){
		seconds = -1;
	}
	else{
		seconds = miliSeconds / 1000 + (0 < miliSeconds % 1000);
	}

	// Init revents.
	for (i = 0; i < nfds; ++i){
		ufds[i].revents = 0;
	}

	// イベントの検索
	for (;;){
		for (i = 0; i < nfds; ++i){
			F_DSC *fd  = getFd(ufds[i].fd);

			if (fd == NULL){
				ufds[i].revents |= POLLNVAL;
				return 1;
			}

			rest = fd->vent->fs->poll(fd->vent->inodeObj, ufds[i].events);
			if (rest < 0){
				ufds[i].revents |= POLLNVAL;
				return 1;
			}
			if (rest & (POLLIN | POLLRDNORM)){
				if (ufds[i].events & (POLLIN | POLLRDNORM)){
					ufds[i].revents |= rest;
				}
			}
			if (rest & (POLLOUT | POLLWRNORM)){
				if (ufds[i].events & (POLLOUT | POLLWRNORM)){
					ufds[i].revents |= rest;
				}
			}
			if (rest & (POLLERR | POLLHUP)){
				ufds[i].revents |= (rest & (POLLERR | POLLHUP));
			}
			if (ufds[i].revents != 0){
				return 1;
			}
		}

		if (seconds == 0){
			break;
		}

		/* sleep */
		sys_sleep(seconds);
		
		/* シグナル割り込みが起きたか？ */
		doSignalRestart();
		if(isSigint() == TRUE){
			return -EINTR;
		}

		seconds = 0;
	}

	return 0;
}


int sys_getstatfs(struct statfs *buf, long bufsize, int flags)
{
	int cnt;

	CALL_PRINT();

	if (buf == NULL){
		cnt = INT_MAX;
	}
	else{
		cnt = bufsize / sizeof(struct statfs);
	}
	
	return searchMount(buf, cnt);
}


int sys_statfs(const char *path, struct statfs *m_statfs)
{
	SEARCH_PARAM start, end;
	VENTRY *vent,*p;
	int rest;

	CALL_PRINT();

	if (*path == '\0'){
		return -ENOENT;
	}
	/* パス長の検査 */
	if (PATH_MAX <= strlen(path)){
		return -ENAMETOOLONG;
	}
	/* バッファの妥当性チェック */
	if (checkMem(m_statfs,sizeof(struct statfs)) == ERR){
		return -EFAULT;
	}

	/* ディレクトリを探す。 */
	start.vent = getTopVdir(path, &start.path);
	if ((rest = getVentObj(&start, &end)) < 0){
		return rest;
	}
	releaseVentObj(end.vent);
	if (*end.path == '\0'){
		if (end.vent->mount == NULL){
			vent = end.vent;
			for (p = vent; p != rootEnt; p = p->parent){
				if (p->din == vent->din){
					break;
				}
			}
			memcpy(m_statfs->f_mntonname, p->name, p->len);	/* directory on which mounted */
			m_statfs->f_mntonname[p->len] = '\0';
		}
		else{
			vent = end.vent->mount;
			memcpy(m_statfs->f_mntonname, vent->name, vent->len);	/* directory on which mounted */
			m_statfs->f_mntonname[vent->len] = '\0';
		}
	}
	else{
		return -ENOENT;
	}

	return getFsstat(vent, m_statfs);
}

/************************************* 未実装 ***************************************/
int sys_lock()
{
	CALL_PRINT();

	return 0;
}


/************************************************************************************
 *
 * 初期化
 *
 ************************************************************************************/


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


// devディレクトリの設定
// return : error number
static int setDevDir(const int devInode, FS *fs, void *rootInode)
{
	uint inodeNum;
	void *inodeObj;
	int type;
	int rest;

	rest = fs->lookup("dev", devInode, rootInode, &inodeNum);
	if (rest < 0){
		return rest;
	}
	rest = fs->open(devInode, inodeNum, O_RDONLY, &inodeObj, &type);
	if (rest < 0){
		return rest;
	}
	
	devEnt->din			= devInode;
	devEnt->fs			= fs;
	devEnt->inodeObj	= inodeObj;
	devEnt->refCount	+= 1;
	
//	fs->close(devInode, inodeObj);
	
	return NOERR;
}


/*=================================== グローバル =======================================*/


/*
 * Init filesystem
 */
void init_fs()
{
	/* rootエントリーの初期化。 */
	rootEnt->fs =		NULL;
	rootEnt->inodeObj =	0;
	rootEnt->next =		rootEnt;
	rootEnt->prev =		rootEnt;
	rootEnt->parent =	rootEnt;
	rootEnt->child =	NULL;
	rootEnt->mount =	NULL;
	rootEnt->wait =		NULL;
	rootEnt->refCount =	2;		// デフォルトカレントディレクトリなので２を設定する。
	rootEnt->din =		0;
	rootEnt->type =		S_IFDIR;
	rootEnt->len =		1;
	rootEnt->name[0] =	'/';

	/* devエントリーの初期化。 */
	devEnt->fs =		NULL;
	devEnt->inodeObj =	0;
	devEnt->next =		devEnt;
	devEnt->prev =		devEnt;
	devEnt->parent =	devEnt;
	devEnt->child =		NULL;
	devEnt->mount =		NULL;
	devEnt->wait =		NULL;
	devEnt->refCount =	1;
	devEnt->din =		0;
	devEnt->type =		S_IFDIR;
	devEnt->len =		3;
	devEnt->name[0] =	'd';
	devEnt->name[1] =	'e';
	devEnt->name[2] =	'v';

	/* Device filesystemエントリーの設定。 */
	rootEnt->child = devEnt;
}


/*
 * Mount device filesystem.
 * parameters : filesystem
 * return : 0 or error number
 */
int mount_dev_fs(FS *fs)
{
	return mountEntry(devEnt, fs, 0, 0);
}


/*
 * mount filesystem
 * parameters : device name,filesystem name
 * return : error number
 */
int mountRoot(const char *devPath, const char *fsName)
{
	int devInode;
	void *rootInodeObj;
	FS *fs;
	int rest;

	/* Search file system number */
	if ((fs = search_fs(fsName)) == NULL){
		return -ENODEV;
	}

	/* デバイスオープン */
	rest = openDevice(devPath, (void**)&devInode);
	if (rest < 0){
		return rest;
	}

	/* マウント関数を呼び出す。 */
	rest = fs->mount(devInode, &rootInodeObj);
	if (rest < 0){
		goto ERR;
	}

	/* ルート仮想ディレクトリの設定。 */
	rootEnt->din = devInode;
	rootEnt->fs = fs;
	rootEnt->inodeObj = rootInodeObj;

	/* "dev"ディレクトリの設定 */
	return setDevDir(devInode, fs, rootInodeObj);

ERR:
	fs->close(0, (void*)devInode);
	return rest;
}


/*
 * マウントページバックアップデバイス。
 * parameters : device name
 * return : device inode or error number
 */
int mountPageBackupDev(const char *devPath, void **o_devInode)
{
	return openDevice(devPath, o_devInode);
}


/*
 * Init process0 file struct.
 * return 0 or error number
 */
int initProc1Fs()
{
	int rest;

	/*
	 * 標準入出力を開く。
	 */
	rest = sys_open("/dev/console", O_RDONLY, 0);	/* 標準入力。 */
	if (rest < 0){
		return rest;
	}
	rest = setFdFlag(rest, 0);						/* No exec fd close. */
	if (rest < 0){
		return rest;
	}
	rest = sys_open("/dev/console", O_WRONLY, 0);	/* 標準出力。 */
	if (rest < 0){
		return rest;
	}
	rest = setFdFlag(rest, 0);
	if (rest < 0){
		return rest;
	}
	rest = sys_open("/dev/console", O_WRONLY, 0);	/* 標準エラー。 */
	if (rest < 0){
		return rest;
	}
	rest = setFdFlag(rest, 0);
	if (rest < 0){
		return rest;
	}

	return 0;
}

/*************************************************************************************************/
int test_fs(uint value1, uint value2, uint value3, uint value4)
{
	printk("test_fs() 0x%x 0x%x 0x%x 0x%x\n", value1, value2, value3, value4);
	return 0;
}
