/*
Copyright (c) 2008-2012 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 <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <limits.h>
#include <fnmatch.h>
#include "glusterfs/glusterfs.h"
#include "glusterfs/common-utils.h"
#include "glusterfs/dict.h"
#define XXH_INLINE_ALL
#include "xxhash.h"
#include "glusterfs/logging.h"
#include "glusterfs/compat.h"
#include "glusterfs/compat-errno.h"
#include "glusterfs/byte-order.h"
#include "glusterfs/globals.h"
#include "glusterfs/statedump.h"
#include "glusterfs/libglusterfs-messages.h"
#include "glusterfs-fops.h"
struct dict_cmp {
dict_t *dict;
gf_boolean_t (*value_ignore)(char *k);
};
#define VALIDATE_DATA_AND_LOG(data, type, key, ret_val) \
do { \
if (!data || !data->data) { \
gf_msg_callingfn("dict", GF_LOG_DEBUG, EINVAL, LG_MSG_INVALID_ARG, \
"data is NULL"); \
return ret_val; \
} \
/* Not of the asked type, or old version */ \
if ((data->data_type != type) && \
(data->data_type != GF_DATA_TYPE_STR_OLD)) { \
gf_msg_callingfn("dict", GF_LOG_DEBUG, EINVAL, LG_MSG_INVALID_ARG, \
"key %s, %s type asked, has %s type", key, \
data_type_name[type], \
data_type_name[data->data_type]); \
} \
} while (0)
static data_t *
get_new_data()
{
data_t *data = mem_get(THIS->ctx->dict_data_pool);
if (!data)
return NULL;
GF_ATOMIC_INIT(data->refcount, 0);
data->is_static = _gf_false;
LOCK_INIT(&data->lock);
return data;
}
static dict_t *
get_new_dict_full(int size_hint)
{
dict_t *dict = mem_get0(THIS->ctx->dict_pool);
if (!dict) {
return NULL;
}
dict->hash_size = size_hint;
if (size_hint == 1) {
/*
* This is the only case we ever see currently. If we ever
* need to support resizing the hash table, the resize function
* will have to take into account the possibility that
* "members" is not separately allocated (i.e. don't just call
* realloc() blindly.
*/
dict->members = &dict->members_internal;
} else {
/*
* We actually need to allocate space for size_hint *pointers*
* but we actually allocate space for one *structure*. Since
* a data_pair_t consists of five pointers, we're wasting four
* pointers' worth for N=1, and will overrun what we allocated
* for N>5. If anybody ever starts using size_hint, we'll need
* to fix this.
*/
GF_ASSERT(size_hint <= (sizeof(data_pair_t) / sizeof(data_pair_t *)));
dict->members = mem_get0(THIS->ctx->dict_pair_pool);
if (!dict->members) {
mem_put(dict);
return NULL;
}
}
LOCK_INIT(&dict->lock);
return dict;
}
dict_t *
dict_new(void)
{
dict_t *dict = get_new_dict_full(1);
if (dict)
dict_ref(dict);
return dict;
}
int32_t
is_data_equal(data_t *one, data_t *two)
{
struct iatt *iatt1, *iatt2;
if (!one || !two || !one->data || !two->data) {
gf_msg_callingfn("dict", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG,
"input arguments are provided "
"with value data_t as NULL");
return -1;
}
if (one == two)
return 1;
if (one->data == two->data)
return 1;
if (one->data_type != two->data_type) {
return 0;
}
if (one->data_type == GF_DATA_TYPE_IATT) {
if ((one->len < sizeof(struct iatt)) ||
(two->len < sizeof(struct iatt))) {
return 0;
}
iatt1 = (struct iatt *)one->data;
iatt2 = (struct iatt *)two->data;
/* Two iatt structs are considered equal if main fields are
* equal, even if times differ.
* TODO: maybe when ctime if fully operational we could
* enforce time matching. */
if (iatt1->ia_ino != iatt2->ia_ino) {
return 0;
}
if (iatt1->ia_type != iatt2->ia_type) {
return 0;
}
if ((iatt1->ia_type == IA_IFBLK) || (iatt1->ia_type == IA_IFCHR)) {
if (iatt1->ia_rdev != iatt2->ia_rdev) {
return 0;
}
}
if (gf_uuid_compare(iatt1->ia_gfid, iatt2->ia_gfid) != 0) {
return 0;
}
/* TODO: ia_uid, ia_gid, ia_prot and ia_size can be changed
* with some commands. Here we don't have enough
* information to decide if they should match or not. */
/*
if ((iatt1->ia_uid != iatt2->ia_uid) ||
(iatt1->ia_gid != iatt2->ia_gid) ||
(st_mode_from_ia(iatt1->ia_prot, iatt1->ia_type) !=
st_mode_from_ia(iatt2->ia_prot,
iatt2->ia_type))) { return 0;
}
if (iatt1->ia_type == IA_IFREG) {
if (iatt1->ia_size != iatt2->ia_size) {
return 0;
}
}
*/
return 1;
}
if (one->len != two->len)
return 0;
if (memcmp(one->data, two->data, one->len) == 0)
return 1;
return 0;
}
static int
key_value_cmp(dict_t *one, char *key1, data_t *value1, void *data)
{
struct dict_cmp *cmp = data;
dict_t *two = cmp->dict;
data_t *value2 = dict_get(two, key1);
if (value2) {
if (cmp->value_ignore && cmp->value_ignore(key1))
return 0;
if (is_data_equal(value1, value2) == 1)
return 0;
}
if (value2 == NULL) {
gf_msg_debug(THIS->name, 0, "'%s' found only on one dict", key1);
} else {
gf_msg_debug(THIS->name, 0,
"'%s' is different in two dicts "
"(%u, %u)",
key1, value1->len, value2->len);
}
return -1;
}
/* If both dicts are NULL then equal. If one of the dicts is NULL but the
* other has only ignorable keys then also they are equal. If both dicts are
* non-null then check if for each non-ignorable key, values are same or
* not. value_ignore function is used to skip comparing values for the keys
* which must be present in both the dictionaries but the value could be
* different.
*/
gf_boolean_t
are_dicts_equal(dict_t *one, dict_t *two,
gf_boolean_t (*match)(dict_t *d, char *k, data_t *v,
void *data),
gf_boolean_t (*value_ignore)(char *k))
{
int num_matches1 = 0;
int num_matches2 = 0;
struct dict_cmp cmp = {0};
if (one == two)
return _gf_true;
if (!match)
match = dict_match_everything;
if ((one == NULL) || (two == NULL)) {
num_matches1 = dict_foreach_match(one ? one : two, match, NULL,
dict_null_foreach_fn, NULL);
goto done;
}
cmp.dict = two;
cmp.value_ignore = value_ignore;
num_matches1 = dict_foreach_match(one, match, NULL, key_value_cmp, &cmp);
if (num_matches1 == -1)
return _gf_false;
if ((num_matches1 == one->count) && (one->count == two->count))
return _gf_true;
num_matches2 = dict_foreach_match(two, match, NULL, dict_null_foreach_fn,
NULL);
done:
/* If the number of matches is same in 'two' then for all the
* valid-keys that exist in 'one' the value matched and no extra valid
* keys exist in 'two' alone. Otherwise there exists at least one extra
* valid-key in 'two' which doesn't exist in 'one' */
if (num_matches1 == num_matches2)
return _gf_true;
return _gf_false;
}
void
data_destroy(data_t *data)
{
if (data) {
LOCK_DESTROY(&data->lock);
if (!data->is_static)
GF_FREE(data->data);
data->len = 0xbabababa;
mem_put(data);
}
}
data_t *
data_copy(data_t *old)
{
if (!old) {
gf_msg_callingfn("dict", GF_LOG_WARNING, 0, LG_MSG_NULL_PTR,
"old is NULL");
return NULL;
}
data_t *newdata = mem_get0(THIS->ctx->dict_data_pool);
if (!newdata) {
return NULL;
}
newdata->len = old->len;
if (old->data) {
newdata->data = memdup(old->data, old->len);
if (!newdata->data)
goto err_out;
}
newdata->data_type = old->data_type;
LOCK_INIT(&newdata->lock);
return newdata;
err_out:
mem_put(newdata);
return NULL;
}
/* Always need to be called under lock
* Always this and key variables are not null -
* checked by callers.
*/
static data_pair_t *
dict_lookup_common(dict_t *this, char *key, uint32_t hash)
{
int hashval = 0;
data_pair_t *pair;
/* If the divisor is 1, the modulo is always 0,
* in such case avoid hash calculation.
*/
if (this->hash_size != 1)
hashval = hash % this->hash_size;
for (pair = this->members[hashval]; pair != NULL; pair = pair->hash_next) {
if (pair->key && (hash == pair->key_hash) && !strcmp(pair->key, key))
return pair;
}
return NULL;
}
int32_t
dict_lookup(dict_t *this, char *key, data_t **data)
{
if (!this || !key || !data) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"!this || !key || "
"!data");
return -1;
}
data_pair_t *tmp = NULL;
uint32_t hash = (uint32_t)XXH64(key, strlen(key), 0);
LOCK(&this->lock);
{
tmp = dict_lookup_common(this, key, hash);
}
UNLOCK(&this->lock);
if (!tmp)
return -1;
*data = tmp->value;
return 0;
}
static int32_t
dict_set_lk(dict_t *this, char *key, data_t *value, const uint32_t hash,
gf_boolean_t replace)
{
int hashval = 0;
data_pair_t *pair;
int key_free = 0;
uint32_t key_hash;
int keylen;
if (!key) {
keylen = gf_asprintf(&key, "ref:%p", value);
if (-1 == keylen) {
return -1;
}
key_free = 1;
key_hash = (uint32_t)XXH64(key, keylen, 0);
} else {
keylen = strlen(key);
key_hash = hash;
}
/* Search for a existing key if 'replace' is asked for */
if (replace) {
pair = dict_lookup_common(this, key, key_hash);
if (pair) {
data_t *unref_data = pair->value;
pair->value = data_ref(value);
data_unref(unref_data);
if (key_free)
GF_FREE(key);
/* Indicates duplicate key */
return 0;
}
}
if (this->free_pair_in_use) {
pair = mem_get(THIS->ctx->dict_pair_pool);
if (!pair) {
if (key_free)
GF_FREE(key);
return -1;
}
} else {
pair = &this->free_pair;
this->free_pair_in_use = _gf_true;
}
if (key_free) {
/* It's ours. Use it. */
pair->key = key;
key_free = 0;
} else {
pair->key = (char *)GF_MALLOC(keylen + 1, gf_common_mt_char);
if (!pair->key) {
if (pair == &this->free_pair) {
this->free_pair_in_use = _gf_false;
} else {
mem_put(pair);
}
return -1;
}
strcpy(pair->key, key);
}
pair->key_hash = key_hash;
pair->value = data_ref(value);
/* If the divisor is 1, the modulo is always 0,
* in such case avoid hash calculation.
*/
if (this->hash_size != 1) {
hashval = (key_hash % this->hash_size);
}
pair->hash_next = this->members[hashval];
this->members[hashval] = pair;
pair->next = this->members_list;
pair->prev = NULL;
if (this->members_list)
this->members_list->prev = pair;
this->members_list = pair;
this->count++;
if (key_free)
GF_FREE(key);
if (this->max_count < this->count)
this->max_count = this->count;
return 0;
}
int32_t
dict_set(dict_t *this, char *key, data_t *value)
{
if (key)
return dict_setn(this, key, strlen(key), value);
else
return dict_setn(this, NULL, 0, value);
}
int32_t
dict_setn(dict_t *this, char *key, const int keylen, data_t *value)
{
int32_t ret;
uint32_t key_hash = 0;
if (!this || !value) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"!this || !value for "
"key=%s",
key);
return -1;
}
if (key) {
key_hash = (int32_t)XXH64(key, keylen, 0);
}
LOCK(&this->lock);
ret = dict_set_lk(this, key, value, key_hash, 1);
UNLOCK(&this->lock);
return ret;
}
int32_t
dict_add(dict_t *this, char *key, data_t *value)
{
if (key)
return dict_addn(this, key, strlen(key), value);
else
return dict_addn(this, NULL, 0, value);
}
int32_t
dict_addn(dict_t *this, char *key, const int keylen, data_t *value)
{
int32_t ret;
uint32_t key_hash = 0;
if (!this || !value) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"!this || !value for key=%s", key);
return -1;
}
if (key) {
key_hash = (uint32_t)XXH64(key, keylen, 0);
}
LOCK(&this->lock);
ret = dict_set_lk(this, key, value, key_hash, 0);
UNLOCK(&this->lock);
return ret;
}
data_t *
dict_get(dict_t *this, char *key)
{
if (!this || !key) {
gf_msg_callingfn("dict", GF_LOG_INFO, EINVAL, LG_MSG_INVALID_ARG,
"!this || key=%s", (key) ? key : "()");
return NULL;
}
return dict_getn(this, key, strlen(key));
}
data_t *
dict_getn(dict_t *this, char *key, const int keylen)
{
data_pair_t *pair;
uint32_t hash;
if (!this || !key) {
gf_msg_callingfn("dict", GF_LOG_INFO, EINVAL, LG_MSG_INVALID_ARG,
"!this || key=%s", (key) ? key : "()");
return NULL;
}
hash = (uint32_t)XXH64(key, keylen, 0);
LOCK(&this->lock);
{
pair = dict_lookup_common(this, key, hash);
}
UNLOCK(&this->lock);
if (pair)
return pair->value;
return NULL;
}
int
dict_key_count(dict_t *this)
{
int ret = -1;
if (!this) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict passed is NULL");
return ret;
}
LOCK(&this->lock);
{
ret = this->count;
}
UNLOCK(&this->lock);
return ret;
}
void
dict_del(dict_t *this, char *key)
{
if (!this || !key) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"!this || key=%s", key);
return;
}
return dict_deln(this, key, strlen(key));
}
void
dict_deln(dict_t *this, char *key, const int keylen)
{
int hashval = 0;
uint32_t hash;
if (!this || !key) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"!this || key=%s", key);
return;
}
hash = (uint32_t)XXH64(key, keylen, 0);
LOCK(&this->lock);
/* If the divisor is 1, the modulo is always 0,
* in such case avoid hash calculation.
*/
if (this->hash_size != 1)
hashval = hash % this->hash_size;
data_pair_t *pair = this->members[hashval];
data_pair_t *prev = NULL;
while (pair) {
if ((hash == pair->key_hash) && strcmp(pair->key, key) == 0) {
if (prev)
prev->hash_next = pair->hash_next;
else
this->members[hashval] = pair->hash_next;
data_unref(pair->value);
if (pair->prev)
pair->prev->next = pair->next;
else
this->members_list = pair->next;
if (pair->next)
pair->next->prev = pair->prev;
GF_FREE(pair->key);
if (pair == &this->free_pair) {
this->free_pair_in_use = _gf_false;
} else {
mem_put(pair);
}
this->count--;
break;
}
prev = pair;
pair = pair->hash_next;
}
UNLOCK(&this->lock);
return;
}
void
dict_destroy(dict_t *this)
{
if (!this) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict is NULL");
return;
}
data_pair_t *pair = this->members_list;
data_pair_t *prev = this->members_list;
glusterfs_ctx_t *ctx = NULL;
uint64_t current_max = 0;
uint32_t total_pairs = 0;
LOCK_DESTROY(&this->lock);
while (prev) {
pair = pair->next;
data_unref(prev->value);
GF_FREE(prev->key);
if (prev != &this->free_pair) {
mem_put(prev);
}
total_pairs++;
prev = pair;
}
if (this->members != &this->members_internal) {
mem_put(this->members);
}
GF_FREE(this->extra_free);
free(this->extra_stdfree);
/* update 'ctx->stats.dict.details' using max_count */
ctx = THIS->ctx;
/* NOTE: below logic is not totaly race proof */
/* thread0 and thread1 gets current_max as 10 */
/* thread0 has 'this->max_count as 11 */
/* thread1 has 'this->max_count as 20 */
/* thread1 goes ahead and sets the max_dict_pairs to 20 */
/* thread0 then goes and sets it to 11 */
/* As it is for information purpose only, no functionality will be
broken by this, but a point to consider about ATOMIC macros. */
current_max = GF_ATOMIC_GET(ctx->stats.max_dict_pairs);
if (current_max < this->max_count)
GF_ATOMIC_INIT(ctx->stats.max_dict_pairs, this->max_count);
GF_ATOMIC_ADD(ctx->stats.total_pairs_used, total_pairs);
GF_ATOMIC_INC(ctx->stats.total_dicts_used);
mem_put(this);
return;
}
void
dict_unref(dict_t *this)
{
uint64_t ref = 0;
if (!this) {
gf_msg_callingfn("dict", GF_LOG_DEBUG, EINVAL, LG_MSG_INVALID_ARG,
"dict is NULL");
return;
}
ref = GF_ATOMIC_DEC(this->refcount);
if (!ref)
dict_destroy(this);
}
dict_t *
dict_ref(dict_t *this)
{
if (!this) {
gf_msg_callingfn("dict", GF_LOG_DEBUG, EINVAL, LG_MSG_INVALID_ARG,
"dict is NULL");
return NULL;
}
GF_ATOMIC_INC(this->refcount);
return this;
}
void
data_unref(data_t *this)
{
uint64_t ref;
if (!this) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"data is NULL");
return;
}
ref = GF_ATOMIC_DEC(this->refcount);
if (!ref)
data_destroy(this);
}
data_t *
data_ref(data_t *this)
{
if (!this) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"data is NULL");
return NULL;
}
GF_ATOMIC_INC(this->refcount);
return this;
}
data_t *
int_to_data(int64_t value)
{
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = gf_asprintf(&data->data, "%" PRId64, value);
if (-1 == data->len) {
gf_msg_debug("dict", 0, "asprintf failed");
return NULL;
}
data->len++; /* account for terminating NULL */
data->data_type = GF_DATA_TYPE_INT;
return data;
}
data_t *
data_from_int64(int64_t value)
{
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = gf_asprintf(&data->data, "%" PRId64, value);
if (-1 == data->len) {
gf_msg_debug("dict", 0, "asprintf failed");
return NULL;
}
data->len++; /* account for terminating NULL */
data->data_type = GF_DATA_TYPE_INT;
return data;
}
data_t *
data_from_int32(int32_t value)
{
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = gf_asprintf(&data->data, "%" PRId32, value);
if (-1 == data->len) {
gf_msg_debug("dict", 0, "asprintf failed");
return NULL;
}
data->len++; /* account for terminating NULL */
data->data_type = GF_DATA_TYPE_INT;
return data;
}
data_t *
data_from_int16(int16_t value)
{
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = gf_asprintf(&data->data, "%" PRId16, value);
if (-1 == data->len) {
gf_msg_debug("dict", 0, "asprintf failed");
return NULL;
}
data->len++; /* account for terminating NULL */
data->data_type = GF_DATA_TYPE_INT;
return data;
}
data_t *
data_from_int8(int8_t value)
{
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = gf_asprintf(&data->data, "%d", value);
if (-1 == data->len) {
gf_msg_debug("dict", 0, "asprintf failed");
return NULL;
}
data->len++; /* account for terminating NULL */
data->data_type = GF_DATA_TYPE_INT;
return data;
}
data_t *
data_from_uint64(uint64_t value)
{
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = gf_asprintf(&data->data, "%" PRIu64, value);
if (-1 == data->len) {
gf_msg_debug("dict", 0, "asprintf failed");
return NULL;
}
data->len++; /* account for terminating NULL */
data->data_type = GF_DATA_TYPE_UINT;
return data;
}
data_t *
data_from_double(double value)
{
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = gf_asprintf(&data->data, "%f", value);
if (data->len == -1) {
return NULL;
}
data->len++; /* account for terminating NULL */
data->data_type = GF_DATA_TYPE_DOUBLE;
return data;
}
data_t *
data_from_uint32(uint32_t value)
{
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = gf_asprintf(&data->data, "%" PRIu32, value);
if (-1 == data->len) {
gf_msg_debug("dict", 0, "asprintf failed");
return NULL;
}
data->len++; /* account for terminating NULL */
data->data_type = GF_DATA_TYPE_UINT;
return data;
}
data_t *
data_from_uint16(uint16_t value)
{
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = gf_asprintf(&data->data, "%" PRIu16, value);
if (-1 == data->len) {
return NULL;
}
data->len++; /* account for terminating NULL */
data->data_type = GF_DATA_TYPE_UINT;
return data;
}
static data_t *
data_from_ptr_common(void *value, gf_boolean_t is_static)
{
/* it is valid to set 0/NULL as a value, no need to check *value */
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->data = value;
data->len = 0;
data->is_static = is_static;
data->data_type = GF_DATA_TYPE_PTR;
return data;
}
data_t *
str_to_data(char *value)
{
if (!value) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"value is NULL");
return NULL;
}
return strn_to_data(value, strlen(value));
}
data_t *
strn_to_data(char *value, const int vallen)
{
if (!value) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"value is NULL");
return NULL;
}
data_t *data = get_new_data();
if (!data) {
return NULL;
}
data->len = vallen + 1;
data->data_type = GF_DATA_TYPE_STR;
data->data = value;
data->is_static = _gf_true;
return data;
}
static data_t *
data_from_dynstr(char *value)
{
if (!value) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"value is NULL");
return NULL;
}
data_t *data = get_new_data();
if (!data)
return NULL;
data->len = strlen(value) + 1;
data->data = value;
data->data_type = GF_DATA_TYPE_STR;
return data;
}
data_t *
data_from_dynptr(void *value, int32_t len)
{
data_t *data = get_new_data();
if (!data)
return NULL;
data->len = len;
data->data = value;
data->data_type = GF_DATA_TYPE_PTR;
return data;
}
data_t *
bin_to_data(void *value, int32_t len)
{
if (!value) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"value is NULL");
return NULL;
}
data_t *data = get_new_data();
if (!data)
return NULL;
data->is_static = _gf_true;
data->len = len;
data->data = value;
return data;
}
static char *data_type_name[GF_DATA_TYPE_MAX] = {
[GF_DATA_TYPE_UNKNOWN] = "unknown",
[GF_DATA_TYPE_STR_OLD] = "string-old-version",
[GF_DATA_TYPE_INT] = "integer",
[GF_DATA_TYPE_UINT] = "unsigned integer",
[GF_DATA_TYPE_DOUBLE] = "float",
[GF_DATA_TYPE_STR] = "string",
[GF_DATA_TYPE_PTR] = "pointer",
[GF_DATA_TYPE_GFUUID] = "gf-uuid",
[GF_DATA_TYPE_IATT] = "iatt",
};
int64_t
data_to_int64(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, "null", -1);
return (int64_t)strtoull(data->data, NULL, 0);
}
int32_t
data_to_int32(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, "null", -1);
return strtoul(data->data, NULL, 0);
}
int16_t
data_to_int16(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, "null", -1);
int16_t value = 0;
errno = 0;
value = strtol(data->data, NULL, 0);
if ((value > SHRT_MAX) || (value < SHRT_MIN)) {
errno = ERANGE;
gf_msg_callingfn("dict", GF_LOG_WARNING, errno,
LG_MSG_DATA_CONVERSION_ERROR,
"Error in data"
" conversion: detected overflow");
return -1;
}
return (int16_t)value;
}
int8_t
data_to_int8(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, "null", -1);
int8_t value = 0;
errno = 0;
value = strtol(data->data, NULL, 0);
if ((value > SCHAR_MAX) || (value < SCHAR_MIN)) {
errno = ERANGE;
gf_msg_callingfn("dict", GF_LOG_WARNING, errno,
LG_MSG_DATA_CONVERSION_ERROR,
"Error in data"
" conversion: detected overflow");
return -1;
}
return (int8_t)value;
}
uint64_t
data_to_uint64(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_UINT, "null", -1);
return strtoll(data->data, NULL, 0);
}
uint32_t
data_to_uint32(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_UINT, "null", -1);
return strtol(data->data, NULL, 0);
}
uint16_t
data_to_uint16(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_UINT, "null", -1);
uint16_t value = 0;
errno = 0;
value = strtol(data->data, NULL, 0);
if ((USHRT_MAX - value) < 0) {
errno = ERANGE;
gf_msg_callingfn("dict", GF_LOG_WARNING, errno,
LG_MSG_DATA_CONVERSION_ERROR,
"Error in data conversion: "
"overflow detected");
return -1;
}
return (uint16_t)value;
}
uint8_t
data_to_uint8(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_UINT, "null", -1);
errno = 0;
uint32_t value = strtol(data->data, NULL, 0);
if ((UCHAR_MAX - (uint8_t)value) < 0) {
errno = ERANGE;
gf_msg_callingfn("dict", GF_LOG_WARNING, errno,
LG_MSG_DATA_CONVERSION_ERROR,
"data "
"conversion overflow detected");
return -1;
}
return (uint8_t)value;
}
char *
data_to_str(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_STR, "null", NULL);
return data->data;
}
void *
data_to_ptr(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_PTR, "null", NULL);
return data->data;
}
void *
data_to_bin(data_t *data)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_PTR, "null", NULL);
return data->data;
}
struct iatt *
data_to_iatt(data_t *data, char *key)
{
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_IATT, key, NULL);
/* We only check for smaller size. If it's bigger we simply ignore
* the extra data. This way it's easy to do changes in the future that
* pass more data but are backward compatible (if the initial contents
* of the struct are maintained, of course). */
if (data->len < sizeof(struct iatt)) {
gf_msg("glusterfs", GF_LOG_ERROR, ENOBUFS, LG_MSG_UNDERSIZED_BUF,
"data value for '%s' is smaller than expected", key);
return NULL;
}
return (struct iatt *)data->data;
}
int
dict_null_foreach_fn(dict_t *d, char *k, data_t *v, void *tmp)
{
return 0;
}
int
dict_remove_foreach_fn(dict_t *d, char *k, data_t *v, void *_tmp)
{
if (!d || !k) {
gf_msg("glusterfs", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ENTRY,
"%s is NULL", d ? "key" : "dictionary");
return -1;
}
dict_del(d, k);
return 0;
}
gf_boolean_t
dict_match_everything(dict_t *d, char *k, data_t *v, void *data)
{
return _gf_true;
}
int
dict_foreach(dict_t *dict,
int (*fn)(dict_t *this, char *key, data_t *value, void *data),
void *data)
{
int ret = dict_foreach_match(dict, dict_match_everything, NULL, fn, data);
if (ret > 0)
ret = 0;
return ret;
}
/* return values:
-1 = failure,
0 = no matches found,
+n = n number of matches
*/
int
dict_foreach_match(dict_t *dict,
gf_boolean_t (*match)(dict_t *this, char *key, data_t *value,
void *mdata),
void *match_data,
int (*action)(dict_t *this, char *key, data_t *value,
void *adata),
void *action_data)
{
if (!dict || !match || !action) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict|match|action is "
"NULL");
return -1;
}
int ret = -1;
int count = 0;
data_pair_t *pairs = dict->members_list;
data_pair_t *next = NULL;
while (pairs) {
next = pairs->next;
if (match(dict, pairs->key, pairs->value, match_data)) {
ret = action(dict, pairs->key, pairs->value, action_data);
if (ret < 0)
return ret;
count++;
}
pairs = next;
}
return count;
}
static gf_boolean_t
dict_fnmatch(dict_t *d, char *k, data_t *val, void *match_data)
{
return (fnmatch(match_data, k, 0) == 0);
}
/* return values:
-1 = failure,
0 = no matches found,
+n = n number of matches
*/
int
dict_foreach_fnmatch(dict_t *dict, char *pattern,
int (*fn)(dict_t *this, char *key, data_t *value,
void *data),
void *data)
{
return dict_foreach_match(dict, dict_fnmatch, pattern, fn, data);
}
/**
* dict_keys_join - pack the keys of the dictionary in a buffer.
*
* @value : buffer in which the keys will be packed (can be NULL)
* @size : size of the buffer which is sent (can be 0, in which case buffer
* is not packed but only length is returned)
* @dict : dictionary of which all the keys will be packed
* @filter_fn : keys matched in filter_fn() is counted.
*
* @return : @length of string after joining keys.
*
*/
int
dict_keys_join(void *value, int size, dict_t *dict, int (*filter_fn)(char *k))
{
int len = 0;
data_pair_t *pairs = dict->members_list;
data_pair_t *next = NULL;
while (pairs) {
next = pairs->next;
if (filter_fn && filter_fn(pairs->key)) {
pairs = next;
continue;
}
if (value && (size > len))
strncpy(value + len, pairs->key, size - len);
len += (strlen(pairs->key) + 1);
pairs = next;
}
return len;
}
static int
dict_copy_one(dict_t *unused, char *key, data_t *value, void *newdict)
{
return dict_set((dict_t *)newdict, key, (value));
}
dict_t *
dict_copy(dict_t *dict, dict_t *new)
{
if (!dict) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict is NULL");
return NULL;
}
if (!new)
new = get_new_dict_full(dict->hash_size);
dict_foreach(dict, dict_copy_one, new);
return new;
}
int
dict_reset(dict_t *dict)
{
int32_t ret = -1;
if (!dict) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict is NULL");
goto out;
}
dict_foreach(dict, dict_remove_foreach_fn, NULL);
ret = 0;
out:
return ret;
}
dict_t *
dict_copy_with_ref(dict_t *dict, dict_t *new)
{
dict_t *local_new = NULL;
GF_VALIDATE_OR_GOTO("dict", dict, fail);
if (new == NULL) {
local_new = dict_new();
GF_VALIDATE_OR_GOTO("dict", local_new, fail);
new = local_new;
}
dict_foreach(dict, dict_copy_one, new);
fail:
return new;
}
/*
* !!!!!!! CLEANED UP CODE !!!!!!!
*/
/**
* Common cleaned up interface:
*
* Return value: 0 success
* -val error, val = errno
*/
int
dict_get_with_ref(dict_t *this, char *key, data_t **data)
{
if (!this || !key || !data) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict OR key (%s) is NULL", key);
return -EINVAL;
}
return dict_get_with_refn(this, key, strlen(key), data);
}
int
dict_get_with_refn(dict_t *this, char *key, const int keylen, data_t **data)
{
data_pair_t *pair = NULL;
int ret = -ENOENT;
uint32_t hash;
if (!this || !key || !data) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict OR key (%s) is NULL", key);
ret = -EINVAL;
goto err;
}
hash = (uint32_t)XXH64(key, keylen, 0);
LOCK(&this->lock);
{
pair = dict_lookup_common(this, key, hash);
if (pair) {
ret = 0;
*data = data_ref(pair->value);
}
}
UNLOCK(&this->lock);
err:
return ret;
}
static int
data_to_ptr_common(data_t *data, void **val)
{
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
*val = data->data;
err:
return ret;
}
static int
data_to_int8_ptr(data_t *data, int8_t *val)
{
int ret = 0;
if (!data || !val) {
ret = -EINVAL;
goto err;
}
errno = 0;
*val = strtol(data->data, NULL, 0);
if (errno != 0)
ret = -errno;
err:
return ret;
}
static int
data_to_int16_ptr(data_t *data, int16_t *val)
{
int ret = 0;
if (!data || !val) {
ret = -EINVAL;
goto err;
}
errno = 0;
*val = strtol(data->data, NULL, 0);
if (errno != 0)
ret = -errno;
err:
return ret;
}
static int
data_to_int32_ptr(data_t *data, int32_t *val)
{
int ret = 0;
if (!data || !val) {
ret = -EINVAL;
goto err;
}
errno = 0;
*val = strtol(data->data, NULL, 0);
if (errno != 0)
ret = -errno;
err:
return ret;
}
static int
data_to_int64_ptr(data_t *data, int64_t *val)
{
int ret = 0;
if (!data || !val) {
ret = -EINVAL;
goto err;
}
errno = 0;
*val = strtoll(data->data, NULL, 0);
if (errno != 0)
ret = -errno;
err:
return ret;
}
static int
data_to_uint16_ptr(data_t *data, uint16_t *val)
{
int ret = 0;
if (!data || !val) {
ret = -EINVAL;
goto err;
}
errno = 0;
*val = strtoul(data->data, NULL, 0);
if (errno != 0)
ret = -errno;
err:
return ret;
}
static int
data_to_uint32_ptr(data_t *data, uint32_t *val)
{
int ret = 0;
if (!data || !val) {
ret = -EINVAL;
goto err;
}
errno = 0;
*val = strtoul(data->data, NULL, 0);
if (errno != 0)
ret = -errno;
err:
return ret;
}
static int
data_to_uint64_ptr(data_t *data, uint64_t *val)
{
int ret = 0;
if (!data || !val) {
ret = -EINVAL;
goto err;
}
errno = 0;
*val = strtoull(data->data, NULL, 0);
if (errno != 0)
ret = -errno;
err:
return ret;
}
static int
data_to_double_ptr(data_t *data, double *val)
{
int ret = 0;
if (!data || !val) {
ret = -EINVAL;
goto err;
}
errno = 0;
*val = strtod(data->data, NULL);
if (errno != 0)
ret = -errno;
err:
return ret;
}
int
dict_get_int8(dict_t *this, char *key, int8_t *val)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !val) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, key, -EINVAL);
ret = data_to_int8_ptr(data, val);
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_int8(dict_t *this, char *key, int8_t val)
{
data_t *data = NULL;
int ret = 0;
data = data_from_int8(val);
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_get_int16(dict_t *this, char *key, int16_t *val)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !val) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, key, -EINVAL);
ret = data_to_int16_ptr(data, val);
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_int16(dict_t *this, char *key, int16_t val)
{
data_t *data = NULL;
int ret = 0;
data = data_from_int16(val);
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_get_int32n(dict_t *this, char *key, const int keylen, int32_t *val)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !val) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_refn(this, key, keylen, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, key, -EINVAL);
ret = data_to_int32_ptr(data, val);
err:
if (data)
data_unref(data);
return ret;
}
int
dict_get_int32(dict_t *this, char *key, int32_t *val)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !val) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, key, -EINVAL);
ret = data_to_int32_ptr(data, val);
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_int32n(dict_t *this, char *key, const int keylen, int32_t val)
{
data_t *data = NULL;
int ret = 0;
data = data_from_int32(val);
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_setn(this, key, keylen, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_set_int32(dict_t *this, char *key, int32_t val)
{
data_t *data = data_from_int32(val);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_get_int64(dict_t *this, char *key, int64_t *val)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !val) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, key, -EINVAL);
ret = data_to_int64_ptr(data, val);
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_int64(dict_t *this, char *key, int64_t val)
{
data_t *data = data_from_int64(val);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_get_uint16(dict_t *this, char *key, uint16_t *val)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !val) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_UINT, key, -EINVAL);
ret = data_to_uint16_ptr(data, val);
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_uint16(dict_t *this, char *key, uint16_t val)
{
data_t *data = data_from_uint16(val);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_get_uint32(dict_t *this, char *key, uint32_t *val)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !val) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_UINT, key, -EINVAL);
ret = data_to_uint32_ptr(data, val);
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_uint32(dict_t *this, char *key, uint32_t val)
{
data_t *data = data_from_uint32(val);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_get_uint64(dict_t *this, char *key, uint64_t *val)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !val) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_UINT, key, -EINVAL);
ret = data_to_uint64_ptr(data, val);
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_uint64(dict_t *this, char *key, uint64_t val)
{
data_t *data = data_from_uint64(val);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
/*
* dict_check_flag can be used to check a one bit flag in an array of flags
* The flag argument indicates the bit position (within the array of bits).
* Currently limited to max of 256 flags for a key.
* return value,
* 1 : flag is set
* 0 : flag is not set
* <0: Error
*/
int
dict_check_flag(dict_t *this, char *key, int flag)
{
data_t *data = NULL;
int ret = -ENOENT;
ret = dict_get_with_ref(this, key, &data);
if (ret < 0) {
return ret;
}
if (BIT_VALUE((unsigned char *)(data->data), flag))
ret = 1;
else
ret = 0;
data_unref(data);
return ret;
}
/*
* _dict_modify_flag can be used to set/clear a bit flag in an array of flags
* flag: indicates the bit position. limited to max of DICT_MAX_FLAGS.
* op: Indicates operation DICT_FLAG_SET / DICT_FLAG_CLEAR
*/
static int
_dict_modify_flag(dict_t *this, char *key, int flag, int op)
{
data_t *data = NULL;
int ret = 0;
data_pair_t *pair = NULL;
char *ptr = NULL;
int hashval = 0;
uint32_t hash;
if (!this || !key) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict OR key (%s) is NULL", key);
ret = -EINVAL;
goto err;
}
/*
* Using a size of 32 bytes to support max of 256
* flags in a single key. This should be suffcient.
*/
GF_ASSERT(flag >= 0 && flag < DICT_MAX_FLAGS);
hash = (int32_t)XXH64(key, strlen(key), 0);
LOCK(&this->lock);
{
pair = dict_lookup_common(this, key, hash);
if (pair) {
data = pair->value;
if (op == DICT_FLAG_SET)
BIT_SET((unsigned char *)(data->data), flag);
else
BIT_CLEAR((unsigned char *)(data->data), flag);
} else {
ptr = GF_CALLOC(1, DICT_MAX_FLAGS / 8, gf_common_mt_char);
if (!ptr) {
gf_msg("dict", GF_LOG_ERROR, ENOMEM, LG_MSG_NO_MEMORY,
"unable to allocate flag bit array");
ret = -ENOMEM;
goto err;
}
data = data_from_dynptr(ptr, DICT_MAX_FLAGS / 8);
if (!data) {
gf_msg("dict", GF_LOG_ERROR, ENOMEM, LG_MSG_NO_MEMORY,
"unable to allocate data");
GF_FREE(ptr);
ret = -ENOMEM;
goto err;
}
if (op == DICT_FLAG_SET)
BIT_SET((unsigned char *)(data->data), flag);
else
BIT_CLEAR((unsigned char *)(data->data), flag);
if (this->free_pair_in_use) {
pair = mem_get0(THIS->ctx->dict_pair_pool);
if (!pair) {
gf_msg("dict", GF_LOG_ERROR, ENOMEM, LG_MSG_NO_MEMORY,
"unable to allocate dict pair");
ret = -ENOMEM;
goto err;
}
} else {
pair = &this->free_pair;
this->free_pair_in_use = _gf_true;
}
pair->key = (char *)GF_MALLOC(strlen(key) + 1, gf_common_mt_char);
if (!pair->key) {
gf_msg("dict", GF_LOG_ERROR, ENOMEM, LG_MSG_NO_MEMORY,
"unable to allocate dict pair");
ret = -ENOMEM;
goto err;
}
strcpy(pair->key, key);
pair->key_hash = hash;
pair->value = data_ref(data);
hashval = hash % this->hash_size;
pair->hash_next = this->members[hashval];
this->members[hashval] = pair;
pair->next = this->members_list;
pair->prev = NULL;
if (this->members_list)
this->members_list->prev = pair;
this->members_list = pair;
this->count++;
if (this->max_count < this->count)
this->max_count = this->count;
}
}
UNLOCK(&this->lock);
return 0;
err:
if (key && this)
UNLOCK(&this->lock);
if (pair) {
if (pair->key)
free(pair->key);
if (pair == &this->free_pair) {
this->free_pair_in_use = _gf_false;
} else {
mem_put(pair);
}
}
if (data)
data_destroy(data);
gf_msg("dict", GF_LOG_ERROR, EINVAL, LG_MSG_DICT_SET_FAILED,
"unable to set key (%s) in dict ", key);
return ret;
}
/*
* Todo:
* Add below primitives as needed:
* dict_check_flags(this, key, flag...): variadic function to check
* multiple flags at a time.
* dict_set_flags(this, key, flag...): set multiple flags
* dict_clear_flags(this, key, flag...): reset multiple flags
*/
int
dict_set_flag(dict_t *this, char *key, int flag)
{
return _dict_modify_flag(this, key, flag, DICT_FLAG_SET);
}
int
dict_clear_flag(dict_t *this, char *key, int flag)
{
return _dict_modify_flag(this, key, flag, DICT_FLAG_CLEAR);
}
int
dict_get_double(dict_t *this, char *key, double *val)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !val) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_DOUBLE, key, -EINVAL);
ret = data_to_double_ptr(data, val);
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_double(dict_t *this, char *key, double val)
{
data_t *data = data_from_double(val);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_set_static_ptr(dict_t *this, char *key, void *ptr)
{
data_t *data = data_from_ptr_common(ptr, _gf_true);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_set_dynptr(dict_t *this, char *key, void *ptr, size_t len)
{
data_t *data = data_from_dynptr(ptr, len);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_get_ptr(dict_t *this, char *key, void **ptr)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !ptr) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_PTR, key, -EINVAL);
ret = data_to_ptr_common(data, ptr);
if (ret != 0) {
goto err;
}
err:
if (data)
data_unref(data);
return ret;
}
int
dict_get_ptr_and_len(dict_t *this, char *key, void **ptr, int *len)
{
data_t *data = NULL;
int ret = 0;
if (!this || !key || !ptr) {
ret = -EINVAL;
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret != 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_PTR, key, -EINVAL);
*len = data->len;
ret = data_to_ptr_common(data, ptr);
if (ret != 0) {
goto err;
}
err:
if (data)
data_unref(data);
return ret;
}
/* Get string - with known key length */
int
dict_get_strn(dict_t *this, char *key, const int keylen, char **str)
{
data_t *data = NULL;
int ret = -EINVAL;
if (!this || !key || !str) {
goto err;
}
ret = dict_get_with_refn(this, key, keylen, &data);
if (ret < 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_STR, key, -EINVAL);
*str = data->data;
err:
if (data)
data_unref(data);
return ret;
}
int
dict_get_str(dict_t *this, char *key, char **str)
{
data_t *data = NULL;
int ret = -EINVAL;
if (!this || !key || !str) {
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret < 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_STR, key, -EINVAL);
*str = data->data;
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_str(dict_t *this, char *key, char *str)
{
data_t *data = str_to_data(str);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
/* Set string - with known key length */
int
dict_set_strn(dict_t *this, char *key, const int keylen, char *str)
{
data_t *data = NULL;
int ret = 0;
data = str_to_data(str);
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_setn(this, key, keylen, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
/* Set string - with known key length and known value length */
int
dict_set_nstrn(dict_t *this, char *key, const int keylen, char *str,
const int vallen)
{
data_t *data = strn_to_data(str, vallen);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_setn(this, key, keylen, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_set_dynstr_with_alloc(dict_t *this, char *key, const char *str)
{
char *alloc_str = gf_strdup(str);
int ret = -1;
if (!alloc_str)
return ret;
ret = dict_set_dynstr(this, key, alloc_str);
if (ret == -EINVAL)
GF_FREE(alloc_str);
return ret;
}
int
dict_set_dynstr(dict_t *this, char *key, char *str)
{
const int keylen = strlen(key);
return dict_set_dynstrn(this, key, keylen, str);
}
int
dict_set_dynstrn(dict_t *this, char *key, const int keylen, char *str)
{
data_t *data = data_from_dynstr(str);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
ret = dict_setn(this, key, keylen, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
/* This function is called only by the volgen for now.
Check how else you can handle it */
int
dict_set_option(dict_t *this, char *key, char *str)
{
data_t *data = data_from_dynstr(str);
int ret = 0;
if (!data) {
ret = -EINVAL;
goto err;
}
data->data_type = GF_DATA_TYPE_STR_OLD;
ret = dict_set(this, key, data);
if (ret < 0)
data_destroy(data);
err:
return ret;
}
int
dict_add_dynstr_with_alloc(dict_t *this, char *key, char *str)
{
data_t *data = NULL;
int ret = 0;
char *alloc_str = gf_strdup(str);
if (!alloc_str)
goto out;
data = data_from_dynstr(alloc_str);
if (!data) {
GF_FREE(alloc_str);
ret = -EINVAL;
goto out;
}
ret = dict_add(this, key, data);
if (ret < 0)
data_destroy(data);
out:
return ret;
}
int
dict_get_bin(dict_t *this, char *key, void **bin)
{
data_t *data = NULL;
int ret = -EINVAL;
if (!this || !key || !bin) {
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret < 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_PTR, key, ret);
*bin = data->data;
err:
if (data)
data_unref(data);
return ret;
}
/********************************************************************
*
* dict_set_bin_common:
* This is the common function to set key and its value in
* dictionary. Flag(is_static) should be set appropriately based
* on the type of memory type used for value(*ptr). If flag is set
* to false value(*ptr) will be freed using GF_FREE() on destroy.
*
*******************************************************************/
static int
dict_set_bin_common(dict_t *this, char *key, void *ptr, size_t size,
gf_boolean_t is_static, gf_dict_data_type_t type)
{
data_t *data = NULL;
int ret = 0;
if (!ptr || (size > DICT_KEY_VALUE_MAX_SIZE)) {
ret = -EINVAL;
goto err;
}
data = bin_to_data(ptr, size);
if (!data) {
ret = -EINVAL;
goto err;
}
data->is_static = is_static;
data->data_type = type;
ret = dict_set(this, key, data);
if (ret < 0) {
/* don't free data->data, let callers handle it */
data->data = NULL;
data_destroy(data);
}
err:
return ret;
}
/********************************************************************
*
* dict_set_bin:
* Set key and its value in the dictionary. This function should
* be called if the value is stored in dynamic memory.
*
*******************************************************************/
int
dict_set_bin(dict_t *this, char *key, void *ptr, size_t size)
{
return dict_set_bin_common(this, key, ptr, size, _gf_false,
GF_DATA_TYPE_PTR);
}
/********************************************************************
*
* dict_set_static_bin:
* Set key and its value in the dictionary. This function should
* be called if the value is stored in static memory.
*
*******************************************************************/
int
dict_set_static_bin(dict_t *this, char *key, void *ptr, size_t size)
{
return dict_set_bin_common(this, key, ptr, size, _gf_true,
GF_DATA_TYPE_PTR);
}
/* */
int
dict_set_gfuuid(dict_t *this, char *key, uuid_t gfid, bool is_static)
{
return dict_set_bin_common(this, key, gfid, sizeof(uuid_t), is_static,
GF_DATA_TYPE_GFUUID);
}
int
dict_get_gfuuid(dict_t *this, char *key, uuid_t *gfid)
{
data_t *data = NULL;
int ret = -EINVAL;
if (!this || !key || !gfid) {
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret < 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_GFUUID, key, -EINVAL);
memcpy(*gfid, data->data, min(data->len, sizeof(uuid_t)));
err:
if (data)
data_unref(data);
return ret;
}
int
dict_set_iatt(dict_t *this, char *key, struct iatt *iatt, bool is_static)
{
return dict_set_bin_common(this, key, iatt, sizeof(struct iatt), is_static,
GF_DATA_TYPE_IATT);
}
int
dict_get_iatt(dict_t *this, char *key, struct iatt *iatt)
{
data_t *data = NULL;
int ret = -EINVAL;
if (!this || !key || !iatt) {
goto err;
}
ret = dict_get_with_ref(this, key, &data);
if (ret < 0) {
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_IATT, key, -EINVAL);
memcpy(iatt, data->data, min(data->len, sizeof(struct iatt)));
err:
if (data)
data_unref(data);
return ret;
}
/**
* dict_get_str_boolean - get a boolean value based on string representation.
*
* @this : dictionary
* @key : dictionary key queried
* @default_val : default value if key not found
*
* @return : @default_val if key not found
* : boolean interpretation of @this[@key] if it makes sense
* (ie., "on", "true", "enable" ...)
* : -1 if error occurs or @this[@key] doesn't make sens as
* boolean
*
* So if you query a boolean option, then via @default_val you can choose
* between following patterns:
*
* - fall back to _gf_false if @key is not set [@default_val = 0]
* - fall back to _gf_true if @key is not set [@default_val = 1]
* - regard as failure if @key is not set [@default_val = -1]
* - handle specially (not as error) if @key is not set
* [@default_val = anything else]
*/
int
dict_get_str_boolean(dict_t *this, char *key, int default_val)
{
data_t *data = NULL;
gf_boolean_t boo = _gf_false;
int ret = 0;
ret = dict_get_with_ref(this, key, &data);
if (ret < 0) {
if (ret == -ENOENT)
ret = default_val;
else
ret = -1;
goto err;
}
VALIDATE_DATA_AND_LOG(data, GF_DATA_TYPE_INT, key, -EINVAL);
ret = gf_string2boolean(data->data, &boo);
if (ret == -1)
goto err;
ret = boo;
err:
if (data)
data_unref(data);
return ret;
}
int
dict_rename_key(dict_t *this, char *key, char *replace_key)
{
data_pair_t *pair = NULL;
int ret = -EINVAL;
uint32_t hash;
uint32_t replacekey_hash;
/* replacing a key by itself is a NO-OP */
if (strcmp(key, replace_key) == 0)
return 0;
if (!this) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict is NULL");
return ret;
}
hash = (uint32_t)XXH64(key, strlen(key), 0);
replacekey_hash = (uint32_t)XXH64(replace_key, strlen(replace_key), 0);
LOCK(&this->lock);
{
/* no need to data_ref(pair->value), dict_set_lk() does it */
pair = dict_lookup_common(this, key, hash);
if (!pair)
ret = -ENODATA;
else
ret = dict_set_lk(this, replace_key, pair->value, replacekey_hash,
1);
}
UNLOCK(&this->lock);
if (!ret)
/* only delete the key on success */
dict_del(this, key);
return ret;
}
/**
* Serialization format:
* -------- -------- -------- ----------- -------------
* | count | key len | val len | key \0| value
* ---------------------------------------- -------------
* 4 4 4 <key len> <value len>
*/
#define DICT_HDR_LEN 4
#define DICT_DATA_HDR_KEY_LEN 4
#define DICT_DATA_HDR_VAL_LEN 4
/**
* dict_serialized_length_lk - return the length of serialized dict. This
* procedure has to be called with this->lock held.
*
* @this : dict to be serialized
* @return: success: len
* : failure: -errno
*/
static int
dict_serialized_length_lk(dict_t *this)
{
int ret = -EINVAL;
int count = this->count;
int len = DICT_HDR_LEN;
data_pair_t *pair = this->members_list;
if (count < 0) {
gf_msg("dict", GF_LOG_ERROR, EINVAL, LG_MSG_COUNT_LESS_THAN_ZERO,
"count (%d) < 0!", count);
goto out;
}
while (count) {
if (!pair) {
gf_msg("dict", GF_LOG_ERROR, EINVAL,
LG_MSG_COUNT_LESS_THAN_DATA_PAIRS,
"less than count data pairs found!");
goto out;
}
len += DICT_DATA_HDR_KEY_LEN + DICT_DATA_HDR_VAL_LEN;
if (!pair->key) {
gf_msg("dict", GF_LOG_ERROR, EINVAL, LG_MSG_NULL_PTR,
"pair->key is null!");
goto out;
}
len += strlen(pair->key) + 1 /* for '\0' */;
if (!pair->value) {
gf_msg("dict", GF_LOG_ERROR, EINVAL, LG_MSG_NULL_PTR,
"pair->value is null!");
goto out;
}
if (pair->value->len < 0) {
gf_msg("dict", GF_LOG_ERROR, EINVAL,
LG_MSG_VALUE_LENGTH_LESS_THAN_ZERO, "value->len (%d) < 0",
pair->value->len);
goto out;
}
len += pair->value->len;
pair = pair->next;
count--;
}
ret = len;
out:
return ret;
}
/**
* dict_serialize_lk - serialize a dictionary into a buffer. This procedure has
* to be called with this->lock held.
*
* @this: dict to serialize
* @buf: buffer to serialize into. This must be
* at least dict_serialized_length (this) large
*
* @return: success: 0
* failure: -errno
*/
static int
dict_serialize_lk(dict_t *this, char *buf)
{
int ret = -1;
data_pair_t *pair = this->members_list;
int32_t count = this->count;
int32_t keylen = 0;
int32_t netword = 0;
if (!buf) {
gf_msg("dict", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG,
"buf is null!");
goto out;
}
if (count < 0) {
gf_msg("dict", GF_LOG_ERROR, 0, LG_MSG_COUNT_LESS_THAN_ZERO,
"count (%d) < 0!", count);
goto out;
}
netword = hton32(count);
memcpy(buf, &netword, sizeof(netword));
buf += DICT_HDR_LEN;
while (count) {
if (!pair) {
gf_msg("dict", GF_LOG_ERROR, 0, LG_MSG_PAIRS_LESS_THAN_COUNT,
"less than count data pairs found!");
goto out;
}
if (!pair->key) {
gf_msg("dict", GF_LOG_ERROR, 0, LG_MSG_NULL_PTR,
"pair->key is null!");
goto out;
}
keylen = strlen(pair->key);
netword = hton32(keylen);
memcpy(buf, &netword, sizeof(netword));
buf += DICT_DATA_HDR_KEY_LEN;
if (!pair->value) {
gf_msg("dict", GF_LOG_ERROR, 0, LG_MSG_NULL_PTR,
"pair->value is null!");
goto out;
}
netword = hton32(pair->value->len);
memcpy(buf, &netword, sizeof(netword));
buf += DICT_DATA_HDR_VAL_LEN;
memcpy(buf, pair->key, keylen);
buf += keylen;
*buf++ = '\0';
if (pair->value->data) {
memcpy(buf, pair->value->data, pair->value->len);
buf += pair->value->len;
}
pair = pair->next;
count--;
}
ret = 0;
out:
return ret;
}
/**
* dict_serialized_length - return the length of serialized dict
*
* @this: dict to be serialized
* @return: success: len
* : failure: -errno
*/
int
dict_serialized_length(dict_t *this)
{
int ret = -EINVAL;
if (!this) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict is null!");
goto out;
}
LOCK(&this->lock);
{
ret = dict_serialized_length_lk(this);
}
UNLOCK(&this->lock);
out:
return ret;
}
/**
* dict_serialize - serialize a dictionary into a buffer
*
* @this: dict to serialize
* @buf: buffer to serialize into. This must be
* at least dict_serialized_length (this) large
*
* @return: success: 0
* failure: -errno
*/
int
dict_serialize(dict_t *this, char *buf)
{
int ret = -1;
if (!this || !buf) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict is null!");
goto out;
}
LOCK(&this->lock);
{
ret = dict_serialize_lk(this, buf);
}
UNLOCK(&this->lock);
out:
return ret;
}
/**
* dict_unserialize - unserialize a buffer into a dict
*
* @buf: buf containing serialized dict
* @size: size of the @buf
* @fill: dict to fill in
*
* @return: success: 0
* failure: -errno
*/
int32_t
dict_unserialize(char *orig_buf, int32_t size, dict_t **fill)
{
char *buf = orig_buf;
int ret = -1;
int32_t count = 0;
int i = 0;
data_t *value = NULL;
char *key = NULL;
int32_t keylen = 0;
int32_t vallen = 0;
int32_t hostord = 0;
if (!buf) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"buf is null!");
goto out;
}
if (size == 0) {
gf_msg_callingfn("dict", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG,
"size is 0!");
goto out;
}
if (!fill) {
gf_msg_callingfn("dict", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG,
"fill is null!");
goto out;
}
if (!*fill) {
gf_msg_callingfn("dict", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG,
"*fill is null!");
goto out;
}
if ((buf + DICT_HDR_LEN) > (orig_buf + size)) {
gf_msg_callingfn("dict", GF_LOG_ERROR, 0, LG_MSG_UNDERSIZED_BUF,
"undersized buffer "
"passed. available (%lu) < required (%lu)",
(long)(orig_buf + size), (long)(buf + DICT_HDR_LEN));
goto out;
}
memcpy(&hostord, buf, sizeof(hostord));
count = ntoh32(hostord);
buf += DICT_HDR_LEN;
if (count < 0) {
gf_msg("dict", GF_LOG_ERROR, 0, LG_MSG_COUNT_LESS_THAN_ZERO,
"count (%d) <= 0", count);
goto out;
}
/* count will be set by the dict_set's below */
(*fill)->count = 0;
for (i = 0; i < count; i++) {
if ((buf + DICT_DATA_HDR_KEY_LEN) > (orig_buf + size)) {
gf_msg_callingfn("dict", GF_LOG_ERROR, 0, LG_MSG_UNDERSIZED_BUF,
"undersized "
"buffer passed. available (%lu) < "
"required (%lu)",
(long)(orig_buf + size),
(long)(buf + DICT_DATA_HDR_KEY_LEN));
goto out;
}
memcpy(&hostord, buf, sizeof(hostord));
keylen = ntoh32(hostord);
buf += DICT_DATA_HDR_KEY_LEN;
if ((buf + DICT_DATA_HDR_VAL_LEN) > (orig_buf + size)) {
gf_msg_callingfn("dict", GF_LOG_ERROR, 0, LG_MSG_UNDERSIZED_BUF,
"undersized "
"buffer passed. available (%lu) < "
"required (%lu)",
(long)(orig_buf + size),
(long)(buf + DICT_DATA_HDR_VAL_LEN));
goto out;
}
memcpy(&hostord, buf, sizeof(hostord));
vallen = ntoh32(hostord);
buf += DICT_DATA_HDR_VAL_LEN;
if ((keylen < 0) || (vallen < 0)) {
gf_msg_callingfn("dict", GF_LOG_ERROR, 0, LG_MSG_UNDERSIZED_BUF,
"undersized length passed "
"key:%d val:%d",
keylen, vallen);
goto out;
}
if ((buf + keylen) > (orig_buf + size)) {
gf_msg_callingfn("dict", GF_LOG_ERROR, 0, LG_MSG_UNDERSIZED_BUF,
"undersized buffer passed. "
"available (%lu) < required (%lu)",
(long)(orig_buf + size), (long)(buf + keylen));
goto out;
}
key = buf;
buf += keylen + 1; /* for '\0' */
if ((buf + vallen) > (orig_buf + size)) {
gf_msg_callingfn("dict", GF_LOG_ERROR, 0, LG_MSG_UNDERSIZED_BUF,
"undersized buffer passed. "
"available (%lu) < required (%lu)",
(long)(orig_buf + size), (long)(buf + vallen));
goto out;
}
value = get_new_data();
if (!value) {
ret = -1;
goto out;
}
value->len = vallen;
value->data = memdup(buf, vallen);
value->data_type = GF_DATA_TYPE_STR_OLD;
value->is_static = _gf_false;
buf += vallen;
ret = dict_add(*fill, key, value);
if (ret < 0)
goto out;
}
ret = 0;
out:
return ret;
}
/**
* dict_allocate_and_serialize - serialize a dictionary into an allocated buffer
*
* @this: dict to serialize
* @buf: pointer to pointer to character. The allocated buffer is stored in
* this pointer. The buffer has to be freed by the caller.
*
* @return: success: 0
* failure: -errno
*/
int32_t
dict_allocate_and_serialize(dict_t *this, char **buf, u_int *length)
{
int ret = -EINVAL;
ssize_t len = 0;
if (!this || !buf) {
gf_msg_debug("dict", 0, "dict OR buf is NULL");
goto out;
}
LOCK(&this->lock);
{
len = dict_serialized_length_lk(this);
if (len < 0) {
ret = len;
goto unlock;
}
*buf = GF_MALLOC(len, gf_common_mt_char);
if (*buf == NULL) {
ret = -ENOMEM;
goto unlock;
}
ret = dict_serialize_lk(this, *buf);
if (ret < 0) {
GF_FREE(*buf);
*buf = NULL;
goto unlock;
}
if (length != NULL) {
*length = len;
}
}
unlock:
UNLOCK(&this->lock);
out:
return ret;
}
/**
* dict_serialize_value_with_delim_lk: serialize the values in the dictionary
* into a buffer separated by delimiter (except the last)
*
* @this : dictionary to serialize
* @buf : the buffer to store the serialized data
* @serz_len : the length of the serialized data (excluding the last delimiter)
* @delimiter : the delimiter to separate the values
*
* @return : 0 -> success
* : -errno -> failure
*/
int
dict_serialize_value_with_delim_lk(dict_t *this, char *buf, int32_t *serz_len,
char delimiter)
{
int ret = -1;
int32_t count = this->count;
int32_t vallen = 0;
int32_t total_len = 0;
data_pair_t *pair = this->members_list;
if (!buf) {
gf_msg("dict", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG, "buf is null");
goto out;
}
if (count < 0) {
gf_msg("dict", GF_LOG_ERROR, EINVAL, LG_MSG_INVALID_ARG,
"count (%d) < 0", count);
goto out;
}
while (count) {
if (!pair) {
gf_msg("dict", GF_LOG_ERROR, 0, LG_MSG_PAIRS_LESS_THAN_COUNT,
"less than count data pairs found");
goto out;
}
if (!pair->key || !pair->value) {
gf_msg("dict", GF_LOG_ERROR, 0, LG_MSG_KEY_OR_VALUE_NULL,
"key or value is null");
goto out;
}
if (!pair->value->data) {
gf_msg("dict", GF_LOG_ERROR, 0, LG_MSG_NULL_VALUE_IN_DICT,
"null value found in dict");
goto out;
}
vallen = pair->value->len - 1; // length includes \0
memcpy(buf, pair->value->data, vallen);
buf += vallen;
*buf++ = delimiter;
total_len += (vallen + 1);
pair = pair->next;
count--;
}
*--buf = '\0'; // remove the last delimiter
total_len--; // adjust the length
ret = 0;
if (serz_len)
*serz_len = total_len;
out:
return ret;
}
int
dict_serialize_value_with_delim(dict_t *this, char *buf, int32_t *serz_len,
char delimiter)
{
int ret = -1;
if (!this || !buf) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict is null!");
goto out;
}
LOCK(&this->lock);
{
ret = dict_serialize_value_with_delim_lk(this, buf, serz_len,
delimiter);
}
UNLOCK(&this->lock);
out:
return ret;
}
int
dict_dump_to_str(dict_t *dict, char *dump, int dumpsize, char *format)
{
int ret = 0;
int dumplen = 0;
data_pair_t *trav = NULL;
if (!dict)
return 0;
for (trav = dict->members_list; trav; trav = trav->next) {
ret = snprintf(&dump[dumplen], dumpsize - dumplen, format, trav->key,
trav->value->data);
if ((ret == -1) || !ret)
return ret;
dumplen += ret;
}
return 0;
}
void
dict_dump_to_log(dict_t *dict)
{
int ret = -1;
char *dump = NULL;
const int dump_size = 64 * 1024;
char *format = "(%s:%s)";
if (!dict) {
gf_msg_callingfn("dict", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict is NULL");
goto out;
}
dump = GF_MALLOC(dump_size, gf_common_mt_char);
if (!dump) {
gf_msg_callingfn("dict", GF_LOG_WARNING, ENOMEM, LG_MSG_NO_MEMORY,
"dump buffer is NULL");
goto out;
}
ret = dict_dump_to_str(dict, dump, dump_size, format);
if (ret) {
gf_msg("dict", GF_LOG_WARNING, 0, LG_MSG_FAILED_TO_LOG_DICT,
"Failed to log dictionary");
goto out;
}
gf_msg("dict", GF_LOG_INFO, 0, LG_MSG_DICT_ERROR, "dict=%p (%s)", dict,
dump);
out:
GF_FREE(dump);
return;
}
void
dict_dump_to_statedump(dict_t *dict, char *dict_name, char *domain)
{
int ret = -1;
char *dump = NULL;
const int dump_size = 64 * 1024;
char key[4096] = {
0,
};
char *format = "\n\t%s:%s";
if (!dict) {
gf_msg_callingfn(domain, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG,
"dict is NULL");
goto out;
}
dump = GF_MALLOC(dump_size, gf_common_mt_char);
if (!dump) {
gf_msg_callingfn(domain, GF_LOG_WARNING, ENOMEM, LG_MSG_NO_MEMORY,
"dump buffer is NULL");
goto out;
}
ret = dict_dump_to_str(dict, dump, dump_size, format);
if (ret) {
gf_msg(domain, GF_LOG_WARNING, 0, LG_MSG_FAILED_TO_LOG_DICT,
"Failed to log dictionary %s", dict_name);
goto out;
}
gf_proc_dump_build_key(key, domain, "%s", dict_name);
gf_proc_dump_write(key, "%s", dump);
out:
GF_FREE(dump);
return;
}
dict_t *
dict_for_key_value(const char *name, const char *value, size_t size,
gf_boolean_t is_static)
{
dict_t *xattr = dict_new();
int ret = 0;
if (!xattr)
return NULL;
if (is_static)
ret = dict_set_static_bin(xattr, (char *)name, (void *)value, size);
else
ret = dict_set_bin(xattr, (char *)name, (void *)value, size);
if (ret) {
dict_destroy(xattr);
xattr = NULL;
}
return xattr;
}
/*
* "strings" should be NULL terminated strings array.
*/
int
dict_has_key_from_array(dict_t *dict, char **strings, gf_boolean_t *result)
{
int i = 0;
uint32_t hash = 0;
if (!dict || !strings || !result)
return -EINVAL;
LOCK(&dict->lock);
{
for (i = 0; strings[i]; i++) {
hash = (uint32_t)XXH64(strings[i], strlen(strings[i]), 0);
if (dict_lookup_common(dict, strings[i], hash)) {
*result = _gf_true;
goto unlock;
}
}
*result = _gf_false;
}
unlock:
UNLOCK(&dict->lock);
return 0;
}