/*
 * fs.c
 *
 * Copyright 2002, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * ファイルシステム上位層
 */


#include"types.h"
#include"config.h"
#include"lib.h"
#include"mm.h"
#include"proc.h"
#include"fs.h"


enum{
	VDIR_MAX_NAME_SIZE=127,		/* 上位ファイルシステムの最大名前サイズ */
	MOUNT_DEV=0,				/* デバイスファイルシステムのdevice inodeは必ず0。 */
};


/* 上位ファイルシステムディレクトリ */
typedef struct VDIR{
	ushort ref_count;				/* 参照カウンタ */
	ushort din;						/* device inode index */
	uint dir_blk;					/* ファイルシステムのディレクトリブロック */
	struct VDIR *next;				/* 同ノード */
	struct VDIR *prev;				/* 同ノード */
	struct VDIR *low_next;			/* 下のノード */
	struct VDIR *high_prev;			/* 上のノード */
	char name[VDIR_MAX_NAME_SIZE];
}VDIR;

/* マウントファイルシステムインデックス */
typedef struct{
	ushort fs_num;			/* ファイルシステムナンバー */
	ushort ref_count;		/* 参照カウント */
}MOUNT_FS;

/* ファイルディスクリプタ */
typedef struct{
	ushort ref_count;	/* 参照カウント */
	ushort din;			/* device inode number */
	uint inode;			/* inodeブロック */
	size_t offset;		/* 読み書き開始オフセット */
}F_DSC;

/* プロセス用ファイル構造体 */
typedef struct{
	VDIR *current_dir;
	F_DSC *fd[MAX_FILE_OPEN];
	uchar num[MAX_FILE_OPEN];
	uchar fd_count;					/* ファイルディスクリプタオープン数 */
	uchar next_fd;					/* 次の空きファイルディスクリプタ */
}FILE_STRUCT;


static FS *fs_info[MAX_REGIST_FS];
static MOUNT_FS mount_fs[MAX_DEVICE_OPEN];
static char _root[sizeof(VDIR)-VDIR_MAX_NAME_SIZE+sizeof(int)];
static VDIR *root=(VDIR*)_root;
static char _dev[sizeof(VDIR)-VDIR_MAX_NAME_SIZE+4];	/* 4は"dev'\0'"の文字列数 */
static VDIR *dev=(VDIR*)_dev;


static VDIR *search_vdir(const char**,VDIR*);
static VDIR *get_vdir(const char*,VDIR*);
static void del_up_vdir(VDIR*);


/************************************************************************************
 *
 * misc functions
 *
 ************************************************************************************/

/*
 * cmpare path strings
 * parameters : Destination string(end='\0'),Sorce string
 * return : 一致=次のソースパス or 不一致=NULL
 */
extern inline const char *cmp_path(const char *path1,const char *path2)
{
	while(*path1!='\0')
		if(*path1++!=*path2++)return NULL;

	if(*path2=='\0')return path2;
	if(*path2=='/')return ++path2;

	return NULL;
}


/*
 * パスを'/'または'\0'の前までコピーして、'\0'を付加する。
 * parameters : Destination string,Sorce string
 */
extern inline const char *cpy_path(const char *path,char *str)
{
	while((*str=*path++)!='\0')
		if(*str++=='/')
		{
			*(str-1)='\0';
			return path;
		}

	return path-1;
}


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

/*
 * Init filesystem
 */
void init_fs()
{
	memset(fs_info,0,sizeof(FS*)*MAX_REGIST_FS);
	memset(mount_fs,0,sizeof(MOUNT_FS)*MAX_DEVICE_OPEN);
	memset(_root,0,sizeof(VDIR)-VDIR_MAX_NAME_SIZE+sizeof(int));
}


/*
 * Mount device file
 * parameters : file system register number
 * return : 0 or error=-1
 */
int mount_dev_fs(int fs)
{
	int mount_dev;
	VDIR *p;


	/* 空きマウントデバイス番号を探す。 */
	if(mount_fs[MOUNT_DEV].ref_count!=0)return -1;
	mount_dev=MOUNT_DEV;
	mount_fs[mount_dev].ref_count=1;
	mount_fs[mount_dev].fs_num=fs;

	/* ディレクトリを追加する。 */
	if(root->low_next==NULL)
	{
		root->low_next=dev;
		dev->next=dev;
		dev->prev=dev;
	}
	else
	{
		p=root->low_next;
		dev->prev=p;
		dev->next=p->next;
		p->next=dev;
		dev->next->prev=dev;
	}
	dev->ref_count=1;
	dev->din=mount_dev;
	dev->dir_blk=0;
	dev->low_next=NULL;
	dev->high_prev=root;
	memcpy(dev->name,"dev",4);

	return 0;
}


/************************************************************************************
 *
 * file system functions
 *
 ************************************************************************************/

/*
 * Register filesystem
 * parameters : Filesystem struct pinter
 * return : 0 or Error=-1
 */
int regist_fs(FS *fs)
{
	int i;


	for(i=0;i<MAX_REGIST_FS;++i)
		if(fs_info[i]==NULL)
		{
			fs_info[i]=fs;
			return 0;
		}

	return -1;
}


/*
 * Search file system
 * parameters : file system name
 * return : file system number or noshing=-1
 */
int search_fs(const char *name)
{
	int i;


	for(i=0;fs_info[i]!=NULL;++i)
		if(strcmp(fs_info[i]->name,name)==0)return i;

	return -1;
}


/*
 * ファイル関係の構造体の初期化。
 * parameters : process
 * return : 0 or error=-1
 */
int init_file_struct(PROC *proc)
{
	int i;
	FILE_STRUCT *p;


	if((p=(FILE_STRUCT*)kmalloc(sizeof(FILE_STRUCT)))==NULL)return -1;
	p->fd_count=0;
	p->next_fd=0;
	for(i=0;i<MAX_FILE_OPEN;++i)p->num[i]=i+1;
	p->current_dir=NULL;
	proc->file_struct=(void*)p;
	memset(p->fd,0,sizeof(F_DSC*)*MAX_FILE_OPEN);

	return 0;
}


/*
 * fork時に、ファイル関係の構造体を親プロセスからコピーする。
 * parameters : parent process,chils process
 * return : 0 or error=-1
 */
int cpy_file_struct(PROC *parent,PROC *child)
{
	int i,j,fd_count;
	F_DSC **fd;


	if((child->file_struct=kmalloc(sizeof(FILE_STRUCT)))==NULL)return -1;
	memcpy(child->file_struct,parent->file_struct,sizeof(FILE_STRUCT));

	/* ファイルディスクリプタの参照カウンタを1増やす。 */
	fd=((FILE_STRUCT*)child->file_struct)->fd;
	fd_count=((FILE_STRUCT*)child->file_struct)->fd_count;
	for(i=0,j=0;;++i)
		if(fd[i]!=NULL)
		{
			++fd[i]->ref_count;
			if(++j>=fd_count)break;
		}

	return 0;
}


/*
 * inodeを返す。
 * parameters : file descriptor
 * return : inode block or error=-1
 */
int get_inode(int fd)
{
	F_DSC *fdsc;


	if(fd>MAX_FILE_OPEN)return -1;

	if((fdsc=((FILE_STRUCT*)(get_current_task()->file_struct))->fd[fd])==NULL)return -1;

	return fdsc->inode;
}


/************************************************************************************
 *
 * ファイルシステム上位層
 * 	マウント時とカレントディレクトリ変更時に、上位ディレクトリを作成する。
 *
 ************************************************************************************/

/*
 * パス名のディレクトリを探す。
 * path address pointer引数に検索した次のpath addressを代入する。
 * parameters : path address pointer,parent vdir
 * return : directory address
 */
VDIR *search_vdir(const char **path,VDIR *vdir)
{
	const char *c;
	VDIR *p,*q;


	for(p=q=vdir->low_next;;p=q,*path=c)
	{
		if((c=cmp_path(".",*path))!=NULL);			/* '.'の場合。 */
		else if((c=cmp_path("..",*path))!=NULL)		/* '..'の場合。 */
			q=p=p->high_prev;
		else
		{
			for(q=p;;)
			{
				if((c=cmp_path(p->name,*path))!=NULL)break;
				if((p=p->next)==q)return NULL;
			}
			q=p->low_next;
		}

		if(*c=='\0')break;
		if(q==NULL)break;
	}

	*path=c;
	return p;
}


/*
 * パス名のディレクトリのポインタを返す。
 * parameters : path,parent directory
 * return : directory pointer or error=NULL
 */
VDIR *get_vdir(const char *path,VDIR *parent)
{
	char str[VDIR_MAX_NAME_SIZE+1];
	const char *c;
	uint block;
	VDIR *p,*q;


	for(p=parent;*path!='\0';)
	{
		/* '.'の場合。 */
		if((c=cmp_path(".",path))!=NULL)path=c;

		/* '..'の場合。 */
		else if((c=cmp_path("..",path))!=NULL)
		{
			q=p->high_prev;

			/* カレントディレクトリが参照されていなければ、削除する。 */
			if((p->ref_count==0)&&(p->low_next==NULL))
			{
				p->next->prev=p->prev;
				p->prev->next=p->next;
				if(q->low_next==p)q->low_next=NULL;
				kfree(p);
			}

			if(q==NULL)return NULL;
			p=q;
			path=c;
		}

		/* 下位ディレクトリがない場合、新規にディレクトリを作成する。 */
		else if(p->low_next==NULL)
		{
			/* 新規ディレクトリの作成。 */
			cpy_path(path,str);
			if((block=fs_info[mount_fs[p->din].fs_num]->opendir(str,p->din,p->dir_blk))==0)
			{
				del_up_vdir(p);
				return NULL;
			}
			if((q=(VDIR*)kmalloc(sizeof(VDIR)-VDIR_MAX_NAME_SIZE+strlen(str)+1))==NULL)
			{
				del_up_vdir(p);
				return NULL;
			}
			p->low_next=q;
			q->ref_count=0;
			q->din=p->din;
			q->dir_blk=block;
			q->next=q;
			q->prev=q;
			q->low_next=NULL;
			q->high_prev=p;
			path=cpy_path(path,q->name);
			p=q;
		}

		else
		{
			for(q=p=p->low_next;;)
			{
				if((c=cmp_path(p->name,path))!=NULL)
				{
					path=c;
					break;
				}
				if((p=p->next)==q)
				{
					/* 新規ディレクトリの作成。 */
					cpy_path(path,str);
					if((block=fs_info[mount_fs[p->din].fs_num]->opendir(str,q->high_prev->din,q->high_prev->dir_blk))==0)return NULL;
					if((q=(VDIR*)kmalloc(sizeof(VDIR)-VDIR_MAX_NAME_SIZE+strlen(str)+1))==NULL)return NULL;
					q->din=p->high_prev->din;
					q->ref_count=0;
					q->dir_blk=block;
					q->next=p->next;
					q->prev=p;
					p->next=q;
					q->next->prev=q;
					q->low_next=NULL;
					q->high_prev=p->high_prev;
					path=cpy_path(path,q->name);
					p=q;

					break;
				}
			}
		}
	}

	return p;
}


/*
 * ディレクトリを溯って削除する。
 * parameters : directory
 */
void del_up_vdir(VDIR *vdir)
{
	VDIR *p,*q;


	for(p=vdir;p!=NULL;)
	{
		/* 参照されてなく、かつ下位ディレクトリがなければ削除する。 */
		if((p->ref_count==0)&&(p->low_next==NULL))
		{
			p->next->prev=p->prev;
			p->prev->next=p->next;
			q=p->high_prev;
			if(q->low_next==p)q->low_next=p->next;
			if(q->low_next==p)q->low_next=NULL;
			kfree(p);
			p=q;
		}
		else break;
	}
}


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

int sys_mount(const char *device,const char *fs_name,const char *path)
{
	enum{DEV_NAME_SIZE=5};		/* "/dev/"のサイズ */

	const char *dev_name="/dev";
	int fs;
	int din;
	int dev_fs;
	uint root_blk;
	VDIR *vdir;


	/* Search file system number */
	if((fs=search_fs(fs_name))==-1)return -1;

	/* デバイスオープン */
	if(cmp_path(dev_name,device)==NULL)return -1;
	vdir=search_vdir(&dev_name,root);
	dev_fs=mount_fs[vdir->din].fs_num;
	if((din=fs_info[dev_fs]->open(device+DEV_NAME_SIZE,0,0))==-1)return -1;

	/* デバイスが既にマウントされているか？ */
	if(mount_fs[din].ref_count>0)goto ERR;

	/* マウント関数を呼び出す。 */
	if((root_blk=fs_info[fs]->mount(din))==-1)goto ERR;

	/* ファイルシステム上位層に登録する。 */
	if(*path=='/')
	{
		if((vdir=get_vdir(path+1,root))==NULL)goto ERR;
	}
	else
		if((vdir=get_vdir(path,((FILE_STRUCT*)get_current_task()->file_struct)->current_dir))==NULL)goto ERR;
	if(vdir->ref_count>0)goto ERR2;
	vdir->din=din;
	vdir->dir_blk=root_blk;
	vdir->ref_count=1;

	return 0;

ERR2:
	del_up_vdir(vdir);
ERR:
	fs_info[dev_fs]->close(MOUNT_DEV,din);

	return -1;
}

int sys_umount(const char *path)
{
	int fs;
	VDIR *vdir;


	/* ディレクトリを探す。 */
	if(*path=='/')
	{
		path=path+1;
		if((vdir=search_vdir(&path,root))==NULL)return -1;
	}
	else
	{
		if((vdir=search_vdir(&path,((FILE_STRUCT*)get_current_task()->file_struct)->current_dir))==NULL)return -1;
	}
	if(*path!='\0')return -1;

	if(vdir->ref_count>1)return -1;

	/* アンマウント関数を呼び出す。 */
	fs=mount_fs[vdir->din].fs_num;
	if(fs_info[fs]->umount(vdir->din)==-1)return -1;

	mount_fs[vdir->din].ref_count=0;
	vdir->ref_count=0;
	del_up_vdir(vdir);

	return 0;
}

/*
 * parameters : path,flag
 * return : file descriptor number or error=-1
 */
int sys_open(const char *path,int flag)
{
	int fd_num;
	uint inode;
	FILE_STRUCT *fstruct;
	F_DSC *fd;
	VDIR *vdir;


	fstruct=(FILE_STRUCT*)get_current_task()->file_struct;
	if(fstruct->fd_count>=MAX_FILE_OPEN)return -1;

	/* ファイルを探す。 */
	if(*path=='/')
	{
		path=path+1;
		if((vdir=search_vdir(&path,root))==NULL)return -1;
	}
	else
	{
		if((vdir=search_vdir(&path,((FILE_STRUCT*)get_current_task()->file_struct)->current_dir))==NULL)return -1;
	}

	if(*path=='\0')return -1;
	if((inode=fs_info[mount_fs[vdir->din].fs_num]->open(path,vdir->din,vdir->dir_blk))==-1)return -1;

	/* ファイルディスクリプターに登録する。 */
	if((fd=(F_DSC*)kmalloc(sizeof(F_DSC)))==NULL)
	{
		fs_info[mount_fs[vdir->din].fs_num]->close(vdir->din,inode);
		return -1;
	}
	fd->din=vdir->din;
	fd->inode=inode;
	fd->offset=0;
	fd->ref_count=1;
	fstruct->fd[fstruct->next_fd]=fd;
	fd_num=fstruct->next_fd;
	fstruct->next_fd=fstruct->num[fstruct->next_fd];
	++fstruct->fd_count;

	return fd_num;
}

/*
 * parameters : file descriptor number
 * return : 0 or error=-1
 */
int sys_close(int fd_num)
{
	FILE_STRUCT *fstruct;
	F_DSC *fd;


	fstruct=(FILE_STRUCT*)get_current_task()->file_struct;
	if((fd=fstruct->fd[fd_num])==NULL)return -1;

	if(--fd->ref_count>0)return 0;										/* 参照カウンタを1減らす。 */
	if(fs_info[mount_fs[fd->din].fs_num]->close(fd->din,fd->inode)==-1)	/* ファイルをクローズする。 */
		return -1;
	kfree(fd);
	fstruct->fd[fd_num]=NULL;
	fstruct->num[fd_num]=fstruct->next_fd;
	fstruct->next_fd=fd_num;
	--fstruct->fd_count;

	return 0;
}

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


	fstruct=(FILE_STRUCT*)get_current_task()->file_struct;
	if((fd=fstruct->fd[fd_num])==NULL)return -1;

	/* read関数を呼び出す。 */
	rest=fs_info[mount_fs[fd->din].fs_num]->read(fd->din,fd->inode,buf,size,fd->offset);
	if(rest==-1)return -1;
	fd->offset+=rest;

	return rest;
}

int sys_write(int fd_num,void *buf,size_t size)
{
	FILE_STRUCT *fstruct;
	F_DSC *fd;
	int rest;


	fstruct=(FILE_STRUCT*)get_current_task()->file_struct;
	if((fd=fstruct->fd[fd_num])==NULL)return -1;

	/* write関数を呼び出す。 */
	rest=fs_info[mount_fs[fd->din].fs_num]->write(fd->din,fd->inode,buf,size,fd->offset);
	if(rest==-1)return -1;
	fd->offset+=rest;

	return rest;
}

int sys_lseek(int fd_num,int offset,int flag)
{
	enum{
		SEEK_SET=0,
		SEEK_CUR,
		SEEK_END
	};

	return 0;
}

int sys_rename()
{
	return 0;
}

int sys_creat()
{
	return 0;
}

int sys_unlink()
{
	return 0;
}

int sys_lock()
{
	return 0;
}

int chdir()
{
	return 0;
}
/*************************************************************************************************/
void test_fs()
{
	uint *buf=(uint*)0x70000;
	int fd;
	FILE_STRUCT *fstruct;


	buf[0]=0xa0a0a0a0;
	buf[0x400-1]=0xa0a0a0a0;

	fstruct=(FILE_STRUCT*)get_current_task()->file_struct;
	if((fd=sys_open("/dev/hdb",0))==-1)
	{
		printk("Failes sys_open()\n");
		return;
	}
	fstruct->fd[fd]->offset=2000*512+5*4;
	if(sys_write(fd,buf,512*8)!=512*8)
	{
		printk("Failed sys_read\n");
		return;
	}
}
/**************************************************************************************************/

