/*
Copyright (c) 2015 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.
*/
#include "ctr-xlator-ctx.h"
#include "ctr-messages.h"
#include <time.h>
#include <sys/time.h>
#define IS_THE_ONLY_HARDLINK(ctr_hard_link) \
(ctr_hard_link->list.next == ctr_hard_link->list.prev)
static void
fini_ctr_hard_link(ctr_hard_link_t **ctr_hard_link)
{
GF_ASSERT(ctr_hard_link);
if (*ctr_hard_link)
return;
GF_FREE((*ctr_hard_link)->base_name);
GF_FREE(*ctr_hard_link);
*ctr_hard_link = NULL;
}
/* Please lock the ctr_xlator_ctx before using this function */
ctr_hard_link_t *
ctr_search_hard_link_ctx(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx,
uuid_t pgfid, const char *base_name)
{
ctr_hard_link_t *_hard_link = NULL;
ctr_hard_link_t *searched_hardlink = NULL;
GF_ASSERT(this);
GF_ASSERT(ctr_xlator_ctx);
if (pgfid == NULL || base_name == NULL)
goto out;
/*linear search*/
list_for_each_entry(_hard_link, &ctr_xlator_ctx->hardlink_list, list)
{
if (gf_uuid_compare(_hard_link->pgfid, pgfid) == 0 &&
_hard_link->base_name &&
strcmp(_hard_link->base_name, base_name) == 0) {
searched_hardlink = _hard_link;
break;
}
}
out:
return searched_hardlink;
}
/* Please lock the ctr_xlator_ctx before using this function */
int
ctr_add_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx,
uuid_t pgfid, const char *base_name)
{
int ret = -1;
ctr_hard_link_t *ctr_hard_link = NULL;
struct timeval current_time = {0};
GF_ASSERT(this);
GF_ASSERT(ctr_xlator_ctx);
if (pgfid == NULL || base_name == NULL)
goto out;
ctr_hard_link = GF_CALLOC(1, sizeof(*ctr_hard_link), gf_ctr_mt_hard_link_t);
if (!ctr_hard_link) {
gf_msg(this->name, GF_LOG_ERROR, ENOMEM, CTR_MSG_CALLOC_FAILED,
"Failed allocating "
"ctr_hard_link");
goto out;
}
/*Initialize the ctr_hard_link object and
* Assign the values : parent GFID and basename*/
INIT_LIST_HEAD(&ctr_hard_link->list);
gf_uuid_copy(ctr_hard_link->pgfid, pgfid);
ret = gf_asprintf(&ctr_hard_link->base_name, "%s", base_name);
if (ret < 0) {
gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_COPY_FAILED,
"Failed copying basename"
"to ctr_hard_link");
goto error;
}
ret = gettimeofday(¤t_time, NULL);
if (ret == -1) {
gf_log(this->name, GF_LOG_ERROR, "Failed to get current time");
goto error;
}
/*Add the hard link to the list*/
list_add_tail(&ctr_hard_link->list, &ctr_xlator_ctx->hardlink_list);
ctr_hard_link->hardlink_heal_period = current_time.tv_sec;
/*aal izz well!*/
ret = 0;
goto out;
error:
GF_FREE(ctr_hard_link);
out:
return ret;
}
static void
__delete_hard_link_from_list(ctr_hard_link_t **ctr_hard_link)
{
GF_ASSERT(ctr_hard_link);
GF_ASSERT(*ctr_hard_link);
/*Remove hard link from list*/
list_del(&(*ctr_hard_link)->list);
fini_ctr_hard_link(ctr_hard_link);
}
int
ctr_delete_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx,
uuid_t pgfid, const char *base_name)
{
int ret = -1;
ctr_hard_link_t *ctr_hard_link = NULL;
GF_ASSERT(this);
GF_ASSERT(ctr_xlator_ctx);
LOCK(&ctr_xlator_ctx->lock);
/*Check if the hard link is present */
ctr_hard_link = ctr_search_hard_link_ctx(this, ctr_xlator_ctx, pgfid,
base_name);
if (!ctr_hard_link) {
gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_HARDLINK_MISSING_IN_LIST,
"Hard link doesn't exist in the list");
goto out;
}
__delete_hard_link_from_list(&ctr_hard_link);
ctr_hard_link = NULL;
ret = 0;
out:
UNLOCK(&ctr_xlator_ctx->lock);
return ret;
}
int
ctr_update_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx,
uuid_t pgfid, const char *base_name, uuid_t old_pgfid,
const char *old_base_name)
{
int ret = -1;
ctr_hard_link_t *ctr_hard_link = NULL;
struct timeval current_time = {0};
GF_ASSERT(this);
GF_ASSERT(ctr_xlator_ctx);
LOCK(&ctr_xlator_ctx->lock);
/*Check if the hard link is present */
ctr_hard_link = ctr_search_hard_link_ctx(this, ctr_xlator_ctx, old_pgfid,
old_base_name);
if (!ctr_hard_link) {
gf_msg_trace(this->name, 0,
"Hard link doesn't exist"
" in the list");
/* Since the hard link is not present in the list
* we add it to the list */
ret = ctr_add_hard_link(this, ctr_xlator_ctx, pgfid, base_name);
if (ret) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_ADD_HARDLINK_TO_LIST_FAILED,
"Failed adding hard link to the list");
goto out;
}
ret = 0;
goto out;
}
/* update the hard link */
gf_uuid_copy(ctr_hard_link->pgfid, pgfid);
GF_FREE(ctr_hard_link->base_name);
ret = gf_asprintf(&ctr_hard_link->base_name, "%s", base_name);
if (ret < 0) {
gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_COPY_FAILED,
"Failed copying basename"
"to ctr_hard_link");
/* delete the corrupted entry */
__delete_hard_link_from_list(&ctr_hard_link);
ctr_hard_link = NULL;
goto out;
}
ret = gettimeofday(¤t_time, NULL);
if (ret == -1) {
gf_log(this->name, GF_LOG_ERROR, "Failed to get current time");
ctr_hard_link->hardlink_heal_period = 0;
} else {
ctr_hard_link->hardlink_heal_period = current_time.tv_sec;
}
ret = 0;
out:
UNLOCK(&ctr_xlator_ctx->lock);
return ret;
}
/* Delete all hardlinks */
static int
ctr_delete_all_hard_link(xlator_t *this, ctr_xlator_ctx_t *ctr_xlator_ctx)
{
int ret = -1;
ctr_hard_link_t *ctr_hard_link = NULL;
ctr_hard_link_t *tmp = NULL;
GF_ASSERT(ctr_xlator_ctx);
LOCK(&ctr_xlator_ctx->lock);
list_for_each_entry_safe(ctr_hard_link, tmp, &ctr_xlator_ctx->hardlink_list,
list)
{
/*Remove hard link from list*/
__delete_hard_link_from_list(&ctr_hard_link);
ctr_hard_link = NULL;
}
UNLOCK(&ctr_xlator_ctx->lock);
ret = 0;
return ret;
}
/* Please lock the inode before using this function */
static ctr_xlator_ctx_t *
__get_ctr_xlator_ctx(xlator_t *this, inode_t *inode)
{
int ret = 0;
uint64_t _addr = 0;
ctr_xlator_ctx_t *ctr_xlator_ctx = NULL;
GF_ASSERT(this);
GF_ASSERT(inode);
ret = __inode_ctx_get(inode, this, &_addr);
if (ret < 0)
_addr = 0;
if (_addr != 0) {
ctr_xlator_ctx = (ctr_xlator_ctx_t *)(long)_addr;
}
return ctr_xlator_ctx;
}
ctr_xlator_ctx_t *
init_ctr_xlator_ctx(xlator_t *this, inode_t *inode)
{
int ret = -1;
uint64_t _addr = 0;
ctr_xlator_ctx_t *ctr_xlator_ctx = NULL;
struct timeval current_time = {0};
GF_ASSERT(this);
GF_ASSERT(inode);
LOCK(&inode->lock);
{
ctr_xlator_ctx = __get_ctr_xlator_ctx(this, inode);
if (ctr_xlator_ctx) {
ret = 0;
goto out;
}
ctr_xlator_ctx = GF_CALLOC(1, sizeof(*ctr_xlator_ctx),
gf_ctr_mt_xlator_ctx);
if (!ctr_xlator_ctx)
goto out;
ret = LOCK_INIT(&ctr_xlator_ctx->lock);
if (ret) {
gf_msg(this->name, GF_LOG_ERROR, ret, CTR_MSG_INIT_LOCK_FAILED,
"Failed init lock %s", strerror(ret));
goto out;
}
_addr = (uint64_t)(uintptr_t)ctr_xlator_ctx;
ret = __inode_ctx_set(inode, this, &_addr);
if (ret) {
goto out;
}
INIT_LIST_HEAD(&ctr_xlator_ctx->hardlink_list);
ret = gettimeofday(¤t_time, NULL);
if (ret == -1) {
gf_log(this->name, GF_LOG_ERROR, "Failed to get current time");
goto out;
}
ctr_xlator_ctx->inode_heal_period = current_time.tv_sec;
}
ret = 0;
out:
if (ret) {
GF_FREE(ctr_xlator_ctx);
ctr_xlator_ctx = NULL;
}
UNLOCK(&inode->lock);
return ctr_xlator_ctx;
}
void
fini_ctr_xlator_ctx(xlator_t *this, inode_t *inode)
{
int ret = 0;
uint64_t _addr = 0;
ctr_xlator_ctx_t *ctr_xlator_ctx = NULL;
inode_ctx_del(inode, this, &_addr);
if (!_addr)
return;
ctr_xlator_ctx = (ctr_xlator_ctx_t *)(long)_addr;
ret = ctr_delete_all_hard_link(this, ctr_xlator_ctx);
if (ret) {
gf_msg(this->name, GF_LOG_WARNING, 0, CTR_MSG_DELETE_HARDLINK_FAILED,
"Failed deleting all "
"hard links from inode context");
}
LOCK_DESTROY(&ctr_xlator_ctx->lock);
GF_FREE(ctr_xlator_ctx);
}
ctr_xlator_ctx_t *
get_ctr_xlator_ctx(xlator_t *this, inode_t *inode)
{
ctr_xlator_ctx_t *ctr_xlator_ctx = NULL;
LOCK(&inode->lock);
ctr_xlator_ctx = __get_ctr_xlator_ctx(this, inode);
UNLOCK(&inode->lock);
return ctr_xlator_ctx;
}