text: /*
   * Acess2 Native Kernel
   * - Acess kernel emulation on another OS using SDL and UDP
   *
   * nativefs.c
   * - Host filesystem access
   */
  #define DEBUG	0
  #define off_t	_acess_off_t
  #define sprintf _acess_sprintf
  #include <acess.h>	// Acess
  #include <vfs.h>	// Acess
  #undef off_t
  #undef sprintf
  #include <dirent.h>	// Posix
  #include <sys/stat.h>	// Posix
  #include <stdio.h>	// C
  
  //NOTES:
  // tVFS_Node->ImplPtr is a pointer to the filesystem flags (tNativeFS)
  // tVFS_Node->Data is the path string (heap string)
  // tVFS_Node->ImplInt is the path length
  
  // === STRUCTURES ===
  typedef struct
  {
  	void	*InodeHandle;
  	 int	bReadOnly;
  }	tNativeFS;
  
  // === PROTOTYPES ===
   int	NativeFS_Install(char **Arguments);
  tVFS_Node	*NativeFS_Mount(const char *Device, const char **Arguments);
  void	NativeFS_Unmount(tVFS_Node *Node);
  tVFS_Node	*NativeFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags);
   int	NativeFS_ReadDir(tVFS_Node *Node, int Position, char Dest[FILENAME_MAX]);
  tVFS_Node	*NativeFS_MkNod(tVFS_Node *Node, const char *Name, Uint Flags);
  size_t	NativeFS_Read(tVFS_Node *Node, _acess_off_t Offset, size_t Length, void *Buffer, Uint Flags);
  size_t	NativeFS_Write(tVFS_Node *Node, _acess_off_t Offset, size_t Length, const void *Buffer, Uint Flags);
  void	NativeFS_Close(tVFS_Node *Node);
  
  // === GLOBALS ===
  tVFS_NodeType	gNativeFS_FileNodeType = {
  	.Read = NativeFS_Read,
  	.Write = NativeFS_Write,
  	.Close = NativeFS_Close
  };
  tVFS_NodeType	gNativeFS_DirNodeType = {
  	.FindDir = NativeFS_FindDir,
  	.ReadDir = NativeFS_ReadDir,
  	.MkNod = NativeFS_MkNod,
  	.Close = NativeFS_Close
  };
  tVFS_Driver	gNativeFS_Driver = {
  	.Name = "nativefs",
  	.InitDevice = NativeFS_Mount,
  	.Unmount = NativeFS_Unmount
  };
  
  // === CODE ===
  int NativeFS_Install(char **Arguments)
  {
  	VFS_AddDriver(&gNativeFS_Driver);
  	return 0;
  }
  
  tVFS_Node *NativeFS_Mount(const char *Device, const char **Arguments)
  {
  	tVFS_Node	*ret;
  	tNativeFS	*info;
  	DIR	*dp;
  	
  	dp = opendir(Device);
  	if(!dp) {
  		Log_Warning("NativeFS", "ERROR: Unable to open device root '%s'", Device);
  		return NULL;
  	}
  	
  	// Check if directory exists
  	// Parse flags from arguments
  	info = malloc(sizeof(tNativeFS));
  	info->InodeHandle = Inode_GetHandle(NULL);
  	info->bReadOnly = 0;
  	// Create node
  	ret = malloc(sizeof(tVFS_Node));
  	memset(ret, 0, sizeof(tVFS_Node));
  	ret->Data = strdup(Device);
  	ret->ImplInt = strlen(ret->Data);
  	ret->ImplPtr = info;
  	ret->Inode = (Uint64)(tVAddr)dp;
  	ret->Flags = VFS_FFLAG_DIRECTORY;
  
  	ret->Type = &gNativeFS_DirNodeType;	
  
  	return ret;
  }
  
  void NativeFS_Unmount(tVFS_Node *Node)
  {
  	tNativeFS	*info = Node->ImplPtr;
  	Inode_ClearCache( info->InodeHandle );
  	closedir( (void *)(tVAddr)Node->Inode );
  	free(Node->Data);
  	free(Node);
  	free(info);
  }
  
  void NativeFS_Close(tVFS_Node *Node)
  {
  	tNativeFS	*info = Node->ImplPtr;
  	DIR	*dp = (Node->Flags & VFS_FFLAG_DIRECTORY) ? (DIR*)(tVAddr)Node->Inode : 0;
  	FILE	*fp = (Node->Flags & VFS_FFLAG_DIRECTORY) ? 0 : (FILE*)(tVAddr)Node->Inode;
  	
  	if( Inode_UncacheNode( info->InodeHandle, Node->Inode ) == 1 ) {
  		if(dp)	closedir(dp);
  		if(fp)	fclose(fp);
  	}
  }
  
  tVFS_Node *NativeFS_FindDir(tVFS_Node *Node, const char *Name, Uint Flags)
  {
  	char	*path;
  	tNativeFS	*info = Node->ImplPtr;
  	tVFS_Node	baseRet;
  	struct stat statbuf;
  
  	ENTER("pNode sName", Node, Name);
  	
  	// Create path
  	path = malloc(Node->ImplInt + 1 + strlen(Name) + 1);
  	strcpy(path, Node->Data);
  	path[Node->ImplInt] = '/';
  	strcpy(path + Node->ImplInt + 1, Name);
  	
  	LOG("path = '%s'", path);
  	
  	// Check if file exists
  	if( stat(path, &statbuf) ) {
  		free(path);
  		LOG("Doesn't exist");
  		LEAVE('n');
  		return NULL;
  	}
  	
  	memset(&baseRet, 0, sizeof(tVFS_Node));
  	
  	// Check file type
  	if( S_ISDIR(statbuf.st_mode) )
  	{
  		LOG("Directory");
  		baseRet.Inode = (Uint64)(tVAddr) opendir(path);
  		baseRet.Type = &gNativeFS_DirNodeType;
  		baseRet.Flags |= VFS_FFLAG_DIRECTORY;
  		baseRet.Size = -1;
  	}
  	else
  	{
  		LOG("File");
  		FILE	*fp = fopen(path, "r+");
  		if( !fp ) {
  			Log_Error("NativeFS", "fopen of '%s' failed: %s", path, strerror(errno));
  			free(path);
  			LEAVE('n');
  			return NULL;
  		}
  		baseRet.Inode = (Uint64)(tVAddr) fp;
  		baseRet.Type = &gNativeFS_FileNodeType;
  		
  		fseek( fp, 0, SEEK_END );
  		baseRet.Size = ftell( fp );
  	}
  	
  	// Create new node
  	baseRet.ImplPtr = info;
  	baseRet.ImplInt = strlen(path);
  	baseRet.Data = path;
  	
  	LEAVE('-');
  	return Inode_CacheNode(info->InodeHandle, &baseRet);
  }
  
  int NativeFS_ReadDir(tVFS_Node *Node, int Position, char Dest[FILENAME_MAX])
  {
  	struct dirent	*ent;
  	DIR	*dp = (void*)(tVAddr)Node->Inode;
  
  	ENTER("pNode iPosition", Node, Position);
  
  	// TODO: Keep track of current position in the directory
  	// TODO: Lock node during this
  	rewinddir(dp);
  	do {
  		ent = readdir(dp);
  	} while(Position-- && ent);
  
  	if( !ent ) {
  		LEAVE('i', -ENOENT);
  		return -ENOENT;
  	}
  	
  	strncpy(Dest, ent->d_name, FILENAME_MAX);
  
  	// TODO: Unlock node	
  
  	LEAVE('i', 0);
  	return 0;
  }
  
  tVFS_Node *NativeFS_MkNod(tVFS_Node *Node, const char *Name, Uint Flags)
  {
  	char path[Node->ImplInt+1+strlen(Name)+1];
  	sprintf(path, "%s/%s", Node->Data, Name);
  	if( Flags & VFS_FFLAG_DIRECTORY )
  	{
  		mkdir(path, 0755);
  	}
  	else
  	{
  		FILE *tmp = fopen(path, "w");
  		if(!tmp)	return NULL;
  		fclose(tmp);
  	}
  	return NativeFS_FindDir(Node, Name, 0);
  }
  
  size_t NativeFS_Read(tVFS_Node *Node, _acess_off_t Offset, size_t Length, void *Buffer, Uint Flags)
  {
  	ENTER("pNode XOffset xLength pBuffer", Node, Offset, Length, Buffer);
  	if( fseek( (FILE *)(tVAddr)Node->Inode, Offset, SEEK_SET ) != 0 )
  	{
  		LEAVE('i', 0);
  		return 0;
  	}
  	size_t ret = fread( Buffer, 1, Length, (FILE *)(tVAddr)Node->Inode );
  	LEAVE('x', ret);
  	return ret;
  }
  
  size_t NativeFS_Write(tVFS_Node *Node, _acess_off_t Offset, size_t Length, const void *Buffer, Uint Flags)
  {
  	FILE	*fp = (FILE *)(tVAddr)Node->Inode;
  	ENTER("pNode XOffset xLength pBuffer", Node, Offset, Length, Buffer);
  	if( fseek( fp, Offset, SEEK_SET ) != 0 )
  	{
  		LEAVE('i', 0);
  		return 0;
  	}
  	size_t ret = fwrite( Buffer, 1, Length, fp );
  	fflush( fp );
  	LEAVE('i', ret);
  	return ret;
  
  }
  