/* 
 * Copyright 2010 Stefan Lankes, Chair for Operating Systems,
 *                               RWTH Aachen University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * This file is part of MetalSVM. 
 */

#include <metalsvm/stdio.h>
#include <metalsvm/string.h>
#include <metalsvm/fs.h>
#include <metalsvm/errno.h>

vfs_node_t* fs_root = NULL;		// The root of the filesystem.

ssize_t read_fs(vfs_node_t* node, uint8_t* buffer, size_t size, off_t offset)
{
	ssize_t ret = -EINVAL;

	if (BUILTIN_EXPECT(!node || !buffer, 0))
		return ret;

	spinlock_lock(&node->lock);
	// Has the node got a read callback?
	if (node->read != 0)
		ret = node->read(node, buffer, size, offset);
	spinlock_unlock(&node->lock);

	return ret;
}

ssize_t write_fs(vfs_node_t* node, uint8_t* buffer, size_t size, off_t offset)
{
	ssize_t ret = -EINVAL;

	if (BUILTIN_EXPECT(!node || !buffer, 0))
		return ret;

	spinlock_lock(&node->lock);
	// Has the node got a write callback?	
	if (node->write != 0)
		ret = node->write(node, buffer, size, offset);
	spinlock_unlock(&node->lock);

	return ret;
}

int open_fs(vfs_node_t* node, uint8_t read, uint8_t write)
{
	int ret = -EINVAL;

	if (BUILTIN_EXPECT(!node, 0))
		return ret;

	spinlock_lock(&node->lock);
	// Has the node got an open callback?
	if (node->open != 0)
		ret = node->open(node);
	spinlock_unlock(&node->lock);

	return ret;
}

int close_fs(vfs_node_t* node)
{
	int ret = -EINVAL;

	if (BUILTIN_EXPECT(!node, 0))
		return ret;

	spinlock_lock(&node->lock);
	// Has the node got a close callback?
	if (node->close != 0)
		ret = node->close(node);
	spinlock_unlock(&node->lock);

	return ret;
}

struct dirent* readdir_fs(vfs_node_t * node, uint32_t index)
{
	struct dirent* ret = NULL;

	if (BUILTIN_EXPECT(!node, 0))
		return ret;

	spinlock_lock(&node->lock);
	// Is the node a directory, and does it have a callback?
	if ((node->type == FS_DIRECTORY) && node->readdir != 0)
		ret = node->readdir(node, index);
	spinlock_unlock(&node->lock);

	return ret;
}

vfs_node_t* finddir_fs(vfs_node_t* node, const char *name)
{
	vfs_node_t* ret = NULL;

	if (BUILTIN_EXPECT(!node, 0))
		return ret;

	spinlock_lock(&node->lock);
	// Is the node a directory, and does it have a callback?
	if ((node->type == FS_DIRECTORY) && node->finddir != 0)
		ret = node->finddir(node, name);
	spinlock_unlock(&node->lock);

	return ret;
}

vfs_node_t* mkdir_fs(vfs_node_t* node, const char *name)
{
	vfs_node_t* ret = NULL;

	if (BUILTIN_EXPECT(!node, 0))
		return ret;

	spinlock_lock(&node->lock);
	if (node->mkdir != 0)
		ret = node->mkdir(node, name);
	spinlock_unlock(&node->lock);

	return ret;
}

vfs_node_t* findnode_fs(const char* name)
{
	uint32_t i, j = 1;
	vfs_node_t* ret = NULL;
	char fname[MAX_FNAME];

	if (BUILTIN_EXPECT(!name, 0))
		return ret;

	if (name[0] == '/')
		ret = fs_root;

	while((name[j] != '\0') && ret) {
		i = 0;
		while((name[j] != '/') && (name[j] != '\0')) {
			fname[i] = name[j];
			i++; j++;
		}
		fname[i] = '\0';
		ret = finddir_fs(ret, fname);
		if (name[j] == '/') 
			j++;
	}

	return ret;
}