/*
   Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com>
   This file is part of GlusterFS.

   This file is licensed to you under your choice of the GNU Lesser
   General Public License, version 3 or any later version (LGPLv3 or
   later), or the GNU General Public License, version 2 (GPLv2), in all
   cases as published by the Free Software Foundation.
*/

#ifndef _CONFIG_H
#define _CONFIG_H
#include "config.h"
#endif

#include "xlator.h"
#include "defaults.h"

#include "meta-mem-types.h"
#include "meta.h"


meta_fd_t *
meta_fd_get (fd_t *fd, xlator_t *this)
{
	uint64_t value = 0;
	meta_fd_t *meta_fd = NULL;

	LOCK (&fd->lock);
	{
		__fd_ctx_get (fd, this, &value);
		if (!value) {
			meta_fd = GF_CALLOC (1, sizeof (*meta_fd),
					     gf_meta_mt_fd_t);
			if (!meta_fd)
				goto unlock;

			value = (long) meta_fd;
			__fd_ctx_set (fd, this, value);
		}

		meta_fd = (void *) value;
	}
unlock:
	UNLOCK (&fd->lock);

	return meta_fd;
}


int
meta_fd_release (fd_t *fd, xlator_t *this)
{
	uint64_t value = 0;
	meta_fd_t *meta_fd = NULL;
	int i = 0;

	fd_ctx_get (fd, this, &value);
	meta_fd = (void *) value;

	if (meta_fd->dirents) {
		for (i = 0; i < meta_fd->size; i++)
			GF_FREE ((void *)meta_fd->dirents[i].name);
		GF_FREE (meta_fd->dirents);
	}

	if (meta_fd) {
		GF_FREE (meta_fd->data);
		GF_FREE (meta_fd);
	}
	return 0;
}


struct meta_ops *
meta_ops_get (inode_t *inode, xlator_t *this)
{
	struct meta_ops *ops = NULL;
	uint64_t value = 0;

	inode_ctx_get2 (inode, this, NULL, &value);

	ops = (void *) value;

	return ops;
}


struct xlator_fops *
meta_fops_get (inode_t *inode, xlator_t *this)
{
	struct meta_ops *ops = NULL;

	ops = meta_ops_get (inode, this);
	if (!ops)
		return default_fops;

	return &ops->fops;
}


int
meta_ops_set (inode_t *inode, xlator_t *this, struct meta_ops *ops)
{
	uint64_t value = 0;
	int ret = 0;

	meta_defaults_init (&ops->fops);

	value = (long) ops;

	ret = inode_ctx_set2 (inode, this, NULL, &value);

	return ret;
}

void *
meta_ctx_get (inode_t *inode, xlator_t *this)
{
	void *ctx = NULL;
	uint64_t value = 0;

	inode_ctx_get2 (inode, this, &value, 0);

	ctx = (void *) value;

	return ctx;
}


int
meta_ctx_set (inode_t *inode, xlator_t *this, void *ctx)
{
	uint64_t value = 0;
	int ret = 0;

	value = (long) ctx;

	ret = inode_ctx_set2 (inode, this, &value, 0);

	return ret;
}


void
meta_local_cleanup (meta_local_t *local, xlator_t *this)
{
	if (!local)
		return;

	if (local->xdata)
		dict_unref (local->xdata);

	GF_FREE (local);
	return;
}


meta_local_t *
meta_local (call_frame_t *frame)
{
	meta_local_t *local = NULL;

	local = frame->local;
	if (!local)
		local = frame->local = GF_CALLOC (1, sizeof(*local),
						  gf_meta_mt_local_t);
	return local;
}


dict_t *
meta_direct_io_mode (dict_t *xdata, call_frame_t *frame)
{
	meta_local_t *local = NULL;

	if (!xdata) {
		local = meta_local (frame);
		if (!local)
			return NULL;
		xdata = local->xdata = dict_new();
		if (!xdata)
			return NULL;
	}

	if (dict_set_int8 (xdata, "direct-io-mode", 1) != 0)
		return NULL;

	return xdata;
}


static uint64_t
gfid_to_ino (uuid_t gfid)
{
        uint64_t  ino = 0;
        int       i = 0, j = 0;

        for (i = 15; i > (15 - 8); i--) {
                ino += (uint64_t)(gfid[i]) << j;
                j += 8;
        }

        return ino;
}


static void
meta_uuid_copy (uuid_t dst, uuid_t src)
{
	gf_uuid_copy (dst, src);
	if (gf_uuid_is_null (dst))
		gf_uuid_generate (dst);
}


static void
default_meta_iatt_fill (struct iatt *iatt, inode_t *inode, ia_type_t type)
{
	struct timeval tv = { };

	iatt->ia_type = type;
	switch (type)
	{
	case IA_IFDIR:
		iatt->ia_prot = ia_prot_from_st_mode (0755);
		iatt->ia_nlink = 2;
		break;
	case IA_IFLNK:
		iatt->ia_prot = ia_prot_from_st_mode (0777);
		iatt->ia_nlink = 1;
		break;
	default:
		iatt->ia_prot = ia_prot_from_st_mode (0644);
		iatt->ia_nlink = 1;
		break;
	}
	iatt->ia_uid = 0;
	iatt->ia_gid = 0;
	iatt->ia_size = 0;

	meta_uuid_copy (iatt->ia_gfid, inode->gfid);
	iatt->ia_ino = gfid_to_ino (iatt->ia_gfid);

	gettimeofday (&tv, 0);
	iatt->ia_mtime = iatt->ia_ctime = iatt->ia_atime = tv.tv_sec;
	iatt->ia_mtime_nsec = iatt->ia_ctime_nsec = iatt->ia_atime_nsec =
		(tv.tv_usec * 1000);
	return;
}


void
meta_iatt_fill (struct iatt *iatt, inode_t *inode, ia_type_t type)
{
	struct meta_ops *ops = NULL;

	ops = meta_ops_get (inode, THIS);
	if (!ops)
		return;

	if (!ops->iatt_fill)
		default_meta_iatt_fill (iatt, inode, type);
	else
		ops->iatt_fill (THIS, inode, iatt);
	return;
}


int
meta_inode_discover (call_frame_t *frame, xlator_t *this, loc_t *loc,
		     dict_t *xdata)
{
	struct iatt iatt = { };
	struct iatt postparent = { };

	meta_iatt_fill (&iatt, loc->inode, loc->inode->ia_type);

	META_STACK_UNWIND (lookup, frame, 0, 0, loc->inode, &iatt, xdata,
			   &postparent);
	return 0;
}


int
meta_file_fill (xlator_t *this, fd_t *fd)
{
	meta_fd_t *meta_fd = NULL;
	strfd_t *strfd = NULL;
	struct meta_ops *ops = NULL;
	int ret = 0;

	meta_fd = meta_fd_get (fd, this);
	if (!meta_fd)
		return -1;

	if (meta_fd->data)
		return meta_fd->size;

	strfd = strfd_open ();
	if (!strfd)
		return -1;

	ops = meta_ops_get (fd->inode, this);
	if (!ops) {
		strfd_close (strfd);
		return -1;
	}

	if (ops->file_fill)
		ret = ops->file_fill (this, fd->inode, strfd);

	if (ret >= 0) {
		meta_fd->data = strfd->data;
		meta_fd->size = strfd->size;

		strfd->data = NULL;
	}

	strfd_close (strfd);

	return meta_fd->size;
}


int
meta_dir_fill (xlator_t *this, fd_t *fd)
{
	meta_fd_t *meta_fd = NULL;
	struct meta_ops *ops = NULL;
	struct meta_dirent *dp = NULL;
	int ret = 0;

	meta_fd = meta_fd_get (fd, this);
	if (!meta_fd)
		return -1;

	if (meta_fd->dirents)
		return meta_fd->size;

	ops = meta_ops_get (fd->inode, this);
	if (!ops)
		return -1;

	if (ops->dir_fill)
		ret = ops->dir_fill (this, fd->inode, &dp);

	if (dp) {
		meta_fd->dirents = dp;
		meta_fd->size = ret;
	}

	return meta_fd->size;
}


int
fixed_dirents_len (struct meta_dirent *dirents)
{
	int i = 0;
	struct meta_dirent *dirent = NULL;

	if (!dirents)
		return 0;

	for (dirent = dirents; dirent->name; dirent++)
		i++;

	return i;
}
