/*
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.
*/
#ifndef __CTR_HELPER_H
#define __CTR_HELPER_H
#include <glusterfs/xlator.h>
#include "ctr_mem_types.h"
#include <glusterfs/iatt.h>
#include <glusterfs/glusterfs.h>
#include <glusterfs/xlator.h>
#include <glusterfs/defaults.h>
#include <glusterfs/logging.h>
#include <glusterfs/common-utils.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#include "gfdb_data_store.h"
#include "ctr-xlator-ctx.h"
#include "ctr-messages.h"
#define CTR_DEFAULT_HARDLINK_EXP_PERIOD 300 /* Five mins */
#define CTR_DEFAULT_INODE_EXP_PERIOD 300 /* Five mins */
typedef struct ctr_query_cbk_args {
int query_fd;
int count;
} ctr_query_cbk_args_t;
/*CTR Xlator Private structure*/
typedef struct gf_ctr_private {
gf_boolean_t enabled;
char *ctr_db_path;
gf_boolean_t ctr_hot_brick;
gf_boolean_t ctr_record_wind;
gf_boolean_t ctr_record_unwind;
gf_boolean_t ctr_record_counter;
gf_boolean_t ctr_record_metadata_heat;
gf_boolean_t ctr_link_consistency;
gfdb_db_type_t gfdb_db_type;
gfdb_sync_type_t gfdb_sync_type;
gfdb_conn_node_t *_db_conn;
uint64_t ctr_lookupheal_link_timeout;
uint64_t ctr_lookupheal_inode_timeout;
gf_boolean_t compact_active;
gf_boolean_t compact_mode_switched;
pthread_mutex_t compact_lock;
} gf_ctr_private_t;
/*
* gf_ctr_local_t is the ctr xlator local data structure that is stored in
* the call_frame of each FOP.
*
* gfdb_db_record: The gf_ctr_local contains a gfdb_db_record object, which is
* used by the insert_record() api from the libgfdb. The gfdb_db_record object
* will contain all the inode and hardlink(only for dentry fops: create,
* mknod,link, unlink, rename).The ctr_local is keep alive till the unwind
* call and will be release during the unwind. The same gfdb_db_record will
* used for the unwind insert_record() api, to record unwind in the database.
*
* ia_inode_type in gf_ctr_local will tell the type of the inode. This is
* important for during the unwind path. As we will not have the inode during
* the unwind path. We would have include this in the gfdb_db_record itself
* but currently we record only file inode information.
*
* is_internal_fop in gf_ctr_local will tell us if this is a internal fop and
* take special/no action. We don't record change/access times or increement
* heat counter for internal fops from rebalancer.
* */
typedef struct gf_ctr_local {
gfdb_db_record_t gfdb_db_record;
ia_type_t ia_inode_type;
gf_boolean_t is_internal_fop;
gf_special_pid_t client_pid;
} gf_ctr_local_t;
/*
* Easy access of gfdb_db_record of ctr_local
* */
#define CTR_DB_REC(ctr_local) (ctr_local->gfdb_db_record)
/*Clear db record*/
#define CLEAR_CTR_DB_RECORD(ctr_local) \
do { \
ctr_local->gfdb_db_record.gfdb_fop_path = GFDB_FOP_INVALID; \
memset(&(ctr_local->gfdb_db_record.gfdb_wind_change_time), 0, \
sizeof(gfdb_time_t)); \
memset(&(ctr_local->gfdb_db_record.gfdb_unwind_change_time), 0, \
sizeof(gfdb_time_t)); \
gf_uuid_clear(ctr_local->gfdb_db_record.gfid); \
gf_uuid_clear(ctr_local->gfdb_db_record.pargfid); \
memset(ctr_local->gfdb_db_record.file_name, 0, GF_NAME_MAX + 1); \
memset(ctr_local->gfdb_db_record.old_file_name, 0, GF_NAME_MAX + 1); \
ctr_local->gfdb_db_record.gfdb_fop_type = GFDB_FOP_INVALID_OP; \
ctr_local->ia_inode_type = IA_INVAL; \
} while (0)
static gf_ctr_local_t *
init_ctr_local_t(xlator_t *this)
{
gf_ctr_local_t *ctr_local = NULL;
GF_ASSERT(this);
ctr_local = mem_get0(this->local_pool);
if (!ctr_local) {
gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0,
CTR_MSG_CREATE_CTR_LOCAL_ERROR_WIND,
"Error while creating ctr local");
goto out;
}
CLEAR_CTR_DB_RECORD(ctr_local);
out:
return ctr_local;
}
static void
free_ctr_local(gf_ctr_local_t *ctr_local)
{
if (ctr_local)
mem_put(ctr_local);
}
/******************************************************************************
*
*
* Context Carrier Structures
*
*
* ****************************************************************************/
/*
* Context Carrier structures are used to carry relevant information about
* inodes and links from the fops calls to the ctr_insert_wind.
* These structure just have pointers to the original data and donot
* do a deep copy of any data. This info is deep copied to
* ctr_local->gfdb_db_record and passed to insert_record() api of libgfdb. This
* info remains persistent for the unwind in ctr_local->gfdb_db_record
* and once used will be destroyed.
*
* gf_ctr_link_context_t : Context structure for hard links
* gf_ctr_inode_context_t : Context structure for inodes
*
* */
/*Context Carrier Structure for hard links*/
typedef struct gf_ctr_link_context {
uuid_t *pargfid;
const char *basename;
} gf_ctr_link_context_t;
/*Context Carrier Structure for inodes*/
typedef struct gf_ctr_inode_context {
ia_type_t ia_type;
uuid_t *gfid;
uuid_t *old_gfid;
gf_ctr_link_context_t *new_link_cx;
gf_ctr_link_context_t *old_link_cx;
gfdb_fop_type_t fop_type;
gfdb_fop_path_t fop_path;
gf_boolean_t is_internal_fop;
/* Indicating metadata fops */
gf_boolean_t is_metadata_fop;
} gf_ctr_inode_context_t;
/*******************Util Macros for Context Carrier Structures*****************/
/*Checks if ctr_link_cx is sane!*/
#define IS_CTR_LINK_CX_SANE(ctr_link_cx) \
do { \
if (ctr_link_cx) { \
if (ctr_link_cx->pargfid) \
GF_ASSERT(*(ctr_link_cx->pargfid)); \
GF_ASSERT(ctr_link_cx->basename); \
}; \
} while (0)
/*Clear and fill the ctr_link_context with values*/
#define FILL_CTR_LINK_CX(ctr_link_cx, _pargfid, _basename, label) \
do { \
GF_VALIDATE_OR_GOTO("ctr", ctr_link_cx, label); \
GF_VALIDATE_OR_GOTO("ctr", _pargfid, label); \
GF_VALIDATE_OR_GOTO("ctr", _basename, label); \
memset(ctr_link_cx, 0, sizeof(*ctr_link_cx)); \
ctr_link_cx->pargfid = &_pargfid; \
ctr_link_cx->basename = _basename; \
} while (0)
#define NEW_LINK_CX(ctr_inode_cx) ctr_inode_cx->new_link_cx
#define OLD_LINK_CX(ctr_inode_cx) ctr_inode_cx->old_link_cx
/*Checks if ctr_inode_cx is sane!*/
#define IS_CTR_INODE_CX_SANE(ctr_inode_cx) \
do { \
GF_ASSERT(ctr_inode_cx); \
GF_ASSERT(ctr_inode_cx->gfid); \
GF_ASSERT(*(ctr_inode_cx->gfid)); \
GF_ASSERT(ctr_inode_cx->fop_type != GFDB_FOP_INVALID_OP); \
GF_ASSERT(ctr_inode_cx->fop_path != GFDB_FOP_INVALID); \
IS_CTR_LINK_CX_SANE(NEW_LINK_CX(ctr_inode_cx)); \
IS_CTR_LINK_CX_SANE(OLD_LINK_CX(ctr_inode_cx)); \
} while (0)
/*Clear and fill the ctr_inode_context with values*/
#define FILL_CTR_INODE_CONTEXT(ctr_inode_cx, _ia_type, _gfid, _new_link_cx, \
_old_link_cx, _fop_type, _fop_path) \
do { \
GF_ASSERT(ctr_inode_cx); \
GF_ASSERT(_gfid); \
GF_ASSERT(_fop_type != GFDB_FOP_INVALID_OP); \
GF_ASSERT(_fop_path != GFDB_FOP_INVALID); \
memset(ctr_inode_cx, 0, sizeof(*ctr_inode_cx)); \
ctr_inode_cx->ia_type = _ia_type; \
ctr_inode_cx->gfid = &_gfid; \
IS_CTR_LINK_CX_SANE(NEW_LINK_CX(ctr_inode_cx)); \
if (_new_link_cx) \
NEW_LINK_CX(ctr_inode_cx) = _new_link_cx; \
IS_CTR_LINK_CX_SANE(OLD_LINK_CX(ctr_inode_cx)); \
if (_old_link_cx) \
OLD_LINK_CX(ctr_inode_cx) = _old_link_cx; \
ctr_inode_cx->fop_type = _fop_type; \
ctr_inode_cx->fop_path = _fop_path; \
} while (0)
/******************************************************************************
*
* Util functions or macros used by
* insert wind and insert unwind
*
* ****************************************************************************/
/* Free ctr frame local */
static inline void
ctr_free_frame_local(call_frame_t *frame)
{
if (frame) {
free_ctr_local((gf_ctr_local_t *)frame->local);
frame->local = NULL;
}
}
/* Setting GF_REQUEST_LINK_COUNT_XDATA in dict
* that has to be sent to POSIX Xlator to send
* link count in unwind path.
* return 0 for success with not creation of dict
* return 1 for success with creation of dict
* return -1 for failure.
* */
static inline int
set_posix_link_request(xlator_t *this, dict_t **xdata)
{
int ret = -1;
gf_boolean_t is_created = _gf_false;
GF_VALIDATE_OR_GOTO("ctr", this, out);
GF_VALIDATE_OR_GOTO(this->name, xdata, out);
/*create xdata if NULL*/
if (!*xdata) {
*xdata = dict_new();
is_created = _gf_true;
ret = 1;
} else {
ret = 0;
}
if (!*xdata) {
gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_XDATA_NULL,
"xdata is NULL :Cannot send "
"GF_REQUEST_LINK_COUNT_XDATA to posix");
ret = -1;
goto out;
}
ret = dict_set_int32(*xdata, GF_REQUEST_LINK_COUNT_XDATA, 1);
if (ret) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_SET_CTR_RESPONSE_LINK_COUNT_XDATA_FAILED,
"Failed setting GF_REQUEST_LINK_COUNT_XDATA");
ret = -1;
goto out;
}
ret = 0;
out:
if (ret == -1) {
if (*xdata && is_created) {
dict_unref(*xdata);
}
}
return ret;
}
/*
* If a bitrot fop
* */
#define BITROT_FOP(frame) \
(frame->root->pid == GF_CLIENT_PID_BITD || \
frame->root->pid == GF_CLIENT_PID_SCRUB)
/*
* If a rebalancer fop
* */
#define REBALANCE_FOP(frame) (frame->root->pid == GF_CLIENT_PID_DEFRAG)
/*
* If its a tiering rebalancer fop
* */
#define TIER_REBALANCE_FOP(frame) \
(frame->root->pid == GF_CLIENT_PID_TIER_DEFRAG)
/*
* If its a AFR SELF HEAL
* */
#define AFR_SELF_HEAL_FOP(frame) (frame->root->pid == GF_CLIENT_PID_SELF_HEALD)
/*
* if a rebalancer fop goto
* */
#define CTR_IF_REBALANCE_FOP_THEN_GOTO(frame, label) \
do { \
if (REBALANCE_FOP(frame)) \
goto label; \
} while (0)
/*
* Internal fop
*
* */
static inline gf_boolean_t
is_internal_fop(call_frame_t *frame, dict_t *xdata)
{
gf_boolean_t ret = _gf_false;
GF_ASSERT(frame);
GF_ASSERT(frame->root);
if (AFR_SELF_HEAL_FOP(frame)) {
ret = _gf_true;
}
if (BITROT_FOP(frame)) {
ret = _gf_true;
}
if (REBALANCE_FOP(frame) || TIER_REBALANCE_FOP(frame)) {
ret = _gf_true;
if (xdata && dict_get(xdata, CTR_ATTACH_TIER_LOOKUP)) {
ret = _gf_false;
}
}
if (xdata && dict_get(xdata, GLUSTERFS_INTERNAL_FOP_KEY)) {
ret = _gf_true;
}
return ret;
}
#define CTR_IF_INTERNAL_FOP_THEN_GOTO(frame, dict, label) \
do { \
if (is_internal_fop(frame, dict)) \
goto label; \
} while (0)
/* if fop has failed exit */
#define CTR_IF_FOP_FAILED_THEN_GOTO(this, op_ret, op_errno, label) \
do { \
if (op_ret == -1) { \
gf_msg_trace(this->name, 0, "Failed fop with %s", \
strerror(op_errno)); \
goto label; \
}; \
} while (0)
/*
* IS CTR Xlator is disabled then goto to label
* */
#define CTR_IS_DISABLED_THEN_GOTO(this, label) \
do { \
gf_ctr_private_t *_priv = NULL; \
GF_ASSERT(this); \
GF_ASSERT(this->private); \
_priv = this->private; \
if (!_priv->_db_conn) \
goto label; \
} while (0)
/*
* IS CTR record metadata heat is disabled then goto to label
* */
#define CTR_RECORD_METADATA_HEAT_IS_DISABLED_THEN_GOTO(this, label) \
do { \
gf_ctr_private_t *_priv = NULL; \
GF_ASSERT(this); \
GF_ASSERT(this->private); \
_priv = this->private; \
if (!_priv->ctr_record_metadata_heat) \
goto label; \
} while (0)
int
fill_db_record_for_unwind(xlator_t *this, gf_ctr_local_t *ctr_local,
gfdb_fop_type_t fop_type, gfdb_fop_path_t fop_path);
int
fill_db_record_for_wind(xlator_t *this, gf_ctr_local_t *ctr_local,
gf_ctr_inode_context_t *ctr_inode_cx);
/*******************************************************************************
* CTR INSERT WIND
* *****************************************************************************
* Function used to insert/update record into the database during a wind fop
* This function creates ctr_local structure into the frame of the fop
* call.
* ****************************************************************************/
static inline int
ctr_insert_wind(call_frame_t *frame, xlator_t *this,
gf_ctr_inode_context_t *ctr_inode_cx)
{
int ret = -1;
gf_ctr_private_t *_priv = NULL;
gf_ctr_local_t *ctr_local = NULL;
GF_ASSERT(frame);
GF_ASSERT(frame->root);
GF_ASSERT(this);
IS_CTR_INODE_CX_SANE(ctr_inode_cx);
_priv = this->private;
GF_ASSERT(_priv);
GF_ASSERT(_priv->_db_conn);
/*If record_wind option of CTR is on record wind for
* regular files only*/
if (_priv->ctr_record_wind && ctr_inode_cx->ia_type != IA_IFDIR) {
frame->local = init_ctr_local_t(this);
if (!frame->local) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_CREATE_CTR_LOCAL_ERROR_WIND,
"WIND: Error while creating ctr local");
goto out;
};
ctr_local = frame->local;
ctr_local->client_pid = frame->root->pid;
ctr_local->is_internal_fop = ctr_inode_cx->is_internal_fop;
/* Decide whether to record counters or not */
CTR_DB_REC(ctr_local).do_record_counters = _gf_false;
/* If record counter is enabled */
if (_priv->ctr_record_counter) {
/* If not a internal fop */
if (!(ctr_local->is_internal_fop)) {
/* If its a metadata fop AND
* record metadata heat
* OR
* its NOT a metadata fop */
if ((ctr_inode_cx->is_metadata_fop &&
_priv->ctr_record_metadata_heat) ||
(!ctr_inode_cx->is_metadata_fop)) {
CTR_DB_REC(ctr_local).do_record_counters = _gf_true;
}
}
}
/* Decide whether to record times or not
* For non internal FOPS record times as usual*/
CTR_DB_REC(ctr_local).do_record_times = _gf_false;
if (!ctr_local->is_internal_fop) {
/* If its a metadata fop AND
* record metadata heat
* OR
* its NOT a metadata fop */
if ((ctr_inode_cx->is_metadata_fop &&
_priv->ctr_record_metadata_heat) ||
(!ctr_inode_cx->is_metadata_fop)) {
CTR_DB_REC(ctr_local).do_record_times =
(_priv->ctr_record_wind || _priv->ctr_record_unwind);
}
}
/* when its a internal FOPS*/
else {
/* Record times only for create
* i.e when the inode is created */
CTR_DB_REC(ctr_local).do_record_times = (isdentrycreatefop(
ctr_inode_cx->fop_type))
? _gf_true
: _gf_false;
}
/*Fill the db record for insertion*/
ret = fill_db_record_for_wind(this, ctr_local, ctr_inode_cx);
if (ret) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_FILL_CTR_LOCAL_ERROR_WIND,
"WIND: Error filling ctr local");
goto out;
}
/*Insert the db record*/
ret = insert_record(_priv->_db_conn, &ctr_local->gfdb_db_record);
if (ret) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_INSERT_RECORD_WIND_FAILED,
"WIND: Inserting of record failed!");
goto out;
}
}
ret = 0;
out:
if (ret) {
free_ctr_local(ctr_local);
frame->local = NULL;
}
return ret;
}
/*******************************************************************************
* CTR INSERT UNWIND
* *****************************************************************************
* Function used to insert/update record into the database during a unwind fop
* This function destroys ctr_local structure into the frame of the fop
* call at the end.
* ****************************************************************************/
static inline int
ctr_insert_unwind(call_frame_t *frame, xlator_t *this, gfdb_fop_type_t fop_type,
gfdb_fop_path_t fop_path)
{
int ret = -1;
gf_ctr_private_t *_priv = NULL;
gf_ctr_local_t *ctr_local = NULL;
GF_ASSERT(frame);
GF_ASSERT(this);
_priv = this->private;
GF_ASSERT(_priv);
GF_ASSERT(_priv->_db_conn);
ctr_local = frame->local;
if (ctr_local && (_priv->ctr_record_unwind || isdentryfop(fop_type)) &&
(ctr_local->ia_inode_type != IA_IFDIR)) {
CTR_DB_REC(ctr_local).do_record_uwind_time = _priv->ctr_record_unwind;
ret = fill_db_record_for_unwind(this, ctr_local, fop_type, fop_path);
if (ret == -1) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_FILL_CTR_LOCAL_ERROR_UNWIND,
"UNWIND: Error filling ctr local");
goto out;
}
ret = insert_record(_priv->_db_conn, &ctr_local->gfdb_db_record);
if (ret == -1) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_FILL_CTR_LOCAL_ERROR_UNWIND,
"UNWIND: Error filling ctr local");
goto out;
}
}
ret = 0;
out:
return ret;
}
/******************************************************************************
* Delete file/flink record/s from db
* ****************************************************************************/
static inline int
ctr_delete_hard_link_from_db(xlator_t *this, uuid_t gfid, uuid_t pargfid,
char *basename, gfdb_fop_type_t fop_type,
gfdb_fop_path_t fop_path)
{
int ret = -1;
gfdb_db_record_t gfdb_db_record;
gf_ctr_private_t *_priv = NULL;
_priv = this->private;
GF_VALIDATE_OR_GOTO(this->name, _priv, out);
GF_VALIDATE_OR_GOTO(this->name, (!gf_uuid_is_null(gfid)), out);
GF_VALIDATE_OR_GOTO(this->name, (!gf_uuid_is_null(pargfid)), out);
GF_VALIDATE_OR_GOTO(this->name, (fop_type == GFDB_FOP_DENTRY_WRITE), out);
GF_VALIDATE_OR_GOTO(
this->name, (fop_path == GFDB_FOP_UNDEL || GFDB_FOP_UNDEL_ALL), out);
/* Set gfdb_db_record to 0 */
memset(&gfdb_db_record, 0, sizeof(gfdb_db_record));
/* Copy basename */
if (snprintf(gfdb_db_record.file_name, GF_NAME_MAX, "%s", basename) >=
GF_NAME_MAX)
goto out;
/* Copy gfid into db record */
gf_uuid_copy(gfdb_db_record.gfid, gfid);
/* Copy pargid into db record */
gf_uuid_copy(gfdb_db_record.pargfid, pargfid);
gfdb_db_record.gfdb_fop_path = fop_path;
gfdb_db_record.gfdb_fop_type = fop_type;
/*send delete request to db*/
ret = insert_record(_priv->_db_conn, &gfdb_db_record);
if (ret) {
gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_INSERT_RECORD_WIND_FAILED,
"Failed to delete record. %s", basename);
goto out;
}
ret = 0;
out:
return ret;
}
/******************************* Hard link function ***************************/
static inline gf_boolean_t
__is_inode_expired(ctr_xlator_ctx_t *ctr_xlator_ctx, gf_ctr_private_t *_priv,
gfdb_time_t *current_time)
{
gf_boolean_t ret = _gf_false;
uint64_t time_diff = 0;
GF_ASSERT(ctr_xlator_ctx);
GF_ASSERT(_priv);
GF_ASSERT(current_time);
time_diff = current_time->tv_sec - ctr_xlator_ctx->inode_heal_period;
ret = (time_diff >= _priv->ctr_lookupheal_inode_timeout) ? _gf_true
: _gf_false;
return ret;
}
static inline gf_boolean_t
__is_hardlink_expired(ctr_hard_link_t *ctr_hard_link, gf_ctr_private_t *_priv,
gfdb_time_t *current_time)
{
gf_boolean_t ret = _gf_false;
uint64_t time_diff = 0;
GF_ASSERT(ctr_hard_link);
GF_ASSERT(_priv);
GF_ASSERT(current_time);
time_diff = current_time->tv_sec - ctr_hard_link->hardlink_heal_period;
ret = ret || (time_diff >= _priv->ctr_lookupheal_link_timeout) ? _gf_true
: _gf_false;
return ret;
}
/* Return values of heal*/
typedef enum ctr_heal_ret_val {
CTR_CTX_ERROR = -1,
/* No healing required */
CTR_TRY_NO_HEAL = 0,
/* Try healing hard link */
CTR_TRY_HARDLINK_HEAL = 1,
/* Try healing inode */
CTR_TRY_INODE_HEAL = 2,
} ctr_heal_ret_val_t;
/**
* @brief Function to add hard link to the inode context variable.
* The inode context maintainences a in-memory list. This is used
* smart healing of database.
* @param frame of the FOP
* @param this is the Xlator instant
* @param inode
* @return Return ctr_heal_ret_val_t
*/
static inline ctr_heal_ret_val_t
add_hard_link_ctx(call_frame_t *frame, xlator_t *this, inode_t *inode)
{
ctr_heal_ret_val_t ret_val = CTR_TRY_NO_HEAL;
int ret = -1;
gf_ctr_local_t *ctr_local = NULL;
ctr_xlator_ctx_t *ctr_xlator_ctx = NULL;
ctr_hard_link_t *ctr_hard_link = NULL;
gf_ctr_private_t *_priv = NULL;
gfdb_time_t current_time = {0};
GF_ASSERT(frame);
GF_ASSERT(this);
GF_ASSERT(inode);
GF_ASSERT(this->private);
_priv = this->private;
ctr_local = frame->local;
if (!ctr_local) {
goto out;
}
ctr_xlator_ctx = init_ctr_xlator_ctx(this, inode);
if (!ctr_xlator_ctx) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_ACCESS_CTR_INODE_CONTEXT_FAILED,
"Failed accessing ctr inode context");
goto out;
}
LOCK(&ctr_xlator_ctx->lock);
/* Check if the hard link already exists
* in the ctr inode context*/
ctr_hard_link = ctr_search_hard_link_ctx(this, ctr_xlator_ctx,
CTR_DB_REC(ctr_local).pargfid,
CTR_DB_REC(ctr_local).file_name);
/* if there then ignore */
if (ctr_hard_link) {
ret = gettimeofday(¤t_time, NULL);
if (ret == -1) {
gf_log(this->name, GF_LOG_ERROR, "Failed to get current time");
ret_val = CTR_CTX_ERROR;
goto unlock;
}
if (__is_hardlink_expired(ctr_hard_link, _priv, ¤t_time)) {
ctr_hard_link->hardlink_heal_period = current_time.tv_sec;
ret_val = ret_val | CTR_TRY_HARDLINK_HEAL;
}
if (__is_inode_expired(ctr_xlator_ctx, _priv, ¤t_time)) {
ctr_xlator_ctx->inode_heal_period = current_time.tv_sec;
ret_val = ret_val | CTR_TRY_INODE_HEAL;
}
goto unlock;
}
/* Add the hard link to the list*/
ret = ctr_add_hard_link(this, ctr_xlator_ctx, CTR_DB_REC(ctr_local).pargfid,
CTR_DB_REC(ctr_local).file_name);
if (ret) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_ADD_HARDLINK_TO_CTR_INODE_CONTEXT_FAILED,
"Failed to add hardlink to the ctr inode context");
ret_val = CTR_CTX_ERROR;
goto unlock;
}
ret_val = CTR_TRY_NO_HEAL;
unlock:
UNLOCK(&ctr_xlator_ctx->lock);
out:
return ret_val;
}
static inline int
delete_hard_link_ctx(call_frame_t *frame, xlator_t *this, inode_t *inode)
{
int ret = -1;
ctr_xlator_ctx_t *ctr_xlator_ctx = NULL;
gf_ctr_local_t *ctr_local = NULL;
GF_ASSERT(frame);
GF_ASSERT(this);
GF_ASSERT(inode);
ctr_local = frame->local;
if (!ctr_local) {
goto out;
}
ctr_xlator_ctx = get_ctr_xlator_ctx(this, inode);
if (!ctr_xlator_ctx) {
/* Since there is no ctr inode context so nothing more to do */
ret = 0;
goto out;
}
ret = ctr_delete_hard_link(this, ctr_xlator_ctx,
CTR_DB_REC(ctr_local).pargfid,
CTR_DB_REC(ctr_local).file_name);
if (ret) {
gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_DELETE_HARDLINK_FAILED,
"Failed to delete hard link");
goto out;
}
ret = 0;
out:
return ret;
}
static inline int
update_hard_link_ctx(call_frame_t *frame, xlator_t *this, inode_t *inode)
{
int ret = -1;
ctr_xlator_ctx_t *ctr_xlator_ctx = NULL;
gf_ctr_local_t *ctr_local = NULL;
GF_ASSERT(frame);
GF_ASSERT(this);
GF_ASSERT(inode);
ctr_local = frame->local;
if (!ctr_local) {
goto out;
}
ctr_xlator_ctx = init_ctr_xlator_ctx(this, inode);
if (!ctr_xlator_ctx) {
gf_msg(this->name, GF_LOG_ERROR, 0,
CTR_MSG_ACCESS_CTR_INODE_CONTEXT_FAILED,
"Failed accessing ctr inode context");
goto out;
}
ret = ctr_update_hard_link(
this, ctr_xlator_ctx, CTR_DB_REC(ctr_local).pargfid,
CTR_DB_REC(ctr_local).file_name, CTR_DB_REC(ctr_local).old_pargfid,
CTR_DB_REC(ctr_local).old_file_name);
if (ret) {
gf_msg(this->name, GF_LOG_ERROR, 0, CTR_MSG_DELETE_HARDLINK_FAILED,
"Failed to delete hard link");
goto out;
}
ret = 0;
out:
return ret;
}
/******************************************************************************
*
* CTR xlator init related functions
*
*
* ****************************************************************************/
int
extract_db_params(xlator_t *this, dict_t *params_dict, gfdb_db_type_t db_type);
int
extract_ctr_options(xlator_t *this, gf_ctr_private_t *_priv);
#endif