/*
  Copyright (c) 2007-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.
*/

#ifndef _GLUSTERFS3_H
#define _GLUSTERFS3_H

#include <sys/uio.h>

#include "xdr-generic.h"
#include "xdr-custom.h"
#include "glusterfs4-xdr.h"
#include <glusterfs/iatt.h>
#include "protocol-common.h"
#include <glusterfs/upcall-utils.h>

#define xdr_decoded_remaining_addr(xdr) ((&xdr)->x_private)
#define xdr_decoded_remaining_len(xdr) ((&xdr)->x_handy)
#define xdr_encoded_length(xdr)                                                \
    (((size_t)(&xdr)->x_private) - ((size_t)(&xdr)->x_base))
#define xdr_decoded_length(xdr)                                                \
    (((size_t)(&xdr)->x_private) - ((size_t)(&xdr)->x_base))

#define GF_O_ACCMODE 003
#define GF_O_RDONLY 00
#define GF_O_WRONLY 01
#define GF_O_RDWR 02
#define GF_O_CREAT 0100
#define GF_O_EXCL 0200
#define GF_O_NOCTTY 0400
#define GF_O_TRUNC 01000
#define GF_O_APPEND 02000
#define GF_O_NONBLOCK 04000
#define GF_O_SYNC 010000
#define GF_O_ASYNC 020000

#define GF_O_DIRECT 040000
#define GF_O_DIRECTORY 0200000
#define GF_O_NOFOLLOW 0400000
#define GF_O_NOATIME 01000000
#define GF_O_CLOEXEC 02000000

#define GF_O_LARGEFILE 0100000

#define GF_O_FMODE_EXEC 040

/* refer 2717 */
#define GF_O_PATH 010000000

#define XLATE_BIT(from, to, bit)                                               \
    do {                                                                       \
        if (from & bit)                                                        \
            to = to | GF_##bit;                                                \
    } while (0)

#define UNXLATE_BIT(from, to, bit)                                             \
    do {                                                                       \
        if (from & GF_##bit)                                                   \
            to = to | bit;                                                     \
    } while (0)

#define XLATE_ACCESSMODE(from, to)                                             \
    do {                                                                       \
        switch (from & O_ACCMODE) {                                            \
            case O_RDONLY:                                                     \
                to |= GF_O_RDONLY;                                             \
                break;                                                         \
            case O_WRONLY:                                                     \
                to |= GF_O_WRONLY;                                             \
                break;                                                         \
            case O_RDWR:                                                       \
                to |= GF_O_RDWR;                                               \
                break;                                                         \
        }                                                                      \
    } while (0)

#define UNXLATE_ACCESSMODE(from, to)                                           \
    do {                                                                       \
        switch (from & GF_O_ACCMODE) {                                         \
            case GF_O_RDONLY:                                                  \
                to |= O_RDONLY;                                                \
                break;                                                         \
            case GF_O_WRONLY:                                                  \
                to |= O_WRONLY;                                                \
                break;                                                         \
            case GF_O_RDWR:                                                    \
                to |= O_RDWR;                                                  \
                break;                                                         \
        }                                                                      \
    } while (0)

static inline uint32_t
gf_flags_from_flags(uint32_t flags)
{
    uint32_t gf_flags = 0;

    XLATE_ACCESSMODE(flags, gf_flags);

    XLATE_BIT(flags, gf_flags, O_CREAT);
    XLATE_BIT(flags, gf_flags, O_EXCL);
    XLATE_BIT(flags, gf_flags, O_NOCTTY);
    XLATE_BIT(flags, gf_flags, O_TRUNC);
    XLATE_BIT(flags, gf_flags, O_APPEND);
    XLATE_BIT(flags, gf_flags, O_NONBLOCK);
    XLATE_BIT(flags, gf_flags, O_SYNC);
    XLATE_BIT(flags, gf_flags, O_ASYNC);

    XLATE_BIT(flags, gf_flags, O_DIRECT);
    XLATE_BIT(flags, gf_flags, O_DIRECTORY);
    XLATE_BIT(flags, gf_flags, O_NOFOLLOW);
#ifdef O_NOATIME
    XLATE_BIT(flags, gf_flags, O_NOATIME);
#endif
#ifdef O_CLOEXEC
    XLATE_BIT(flags, gf_flags, O_CLOEXEC);
#endif
    XLATE_BIT(flags, gf_flags, O_LARGEFILE);
    XLATE_BIT(flags, gf_flags, O_FMODE_EXEC);
#ifdef O_PATH
    XLATE_BIT(flags, gf_flags, O_PATH);
#endif

    return gf_flags;
}

static inline uint32_t
gf_flags_to_flags(uint32_t gf_flags)
{
    uint32_t flags = 0;

    UNXLATE_ACCESSMODE(gf_flags, flags);

    UNXLATE_BIT(gf_flags, flags, O_CREAT);
    UNXLATE_BIT(gf_flags, flags, O_EXCL);
    UNXLATE_BIT(gf_flags, flags, O_NOCTTY);
    UNXLATE_BIT(gf_flags, flags, O_TRUNC);
    UNXLATE_BIT(gf_flags, flags, O_APPEND);
    UNXLATE_BIT(gf_flags, flags, O_NONBLOCK);
    UNXLATE_BIT(gf_flags, flags, O_SYNC);
    UNXLATE_BIT(gf_flags, flags, O_ASYNC);

    UNXLATE_BIT(gf_flags, flags, O_DIRECT);
    UNXLATE_BIT(gf_flags, flags, O_DIRECTORY);
    UNXLATE_BIT(gf_flags, flags, O_NOFOLLOW);
#ifdef O_NOATIME
    UNXLATE_BIT(gf_flags, flags, O_NOATIME);
#endif
#ifdef O_CLOEXEC
    UNXLATE_BIT(gf_flags, flags, O_CLOEXEC);
#endif
    UNXLATE_BIT(gf_flags, flags, O_LARGEFILE);
    UNXLATE_BIT(gf_flags, flags, O_FMODE_EXEC);
#ifdef O_PATH
    UNXLATE_BIT(gf_flags, flags, O_PATH);
#endif

    return flags;
}

static inline void
gf_statfs_to_statfs(struct gf_statfs *gf_stat, struct statvfs *stat)
{
    if (!stat || !gf_stat)
        return;

    stat->f_bsize = (gf_stat->bsize);
    stat->f_frsize = (gf_stat->frsize);
    stat->f_blocks = (gf_stat->blocks);
    stat->f_bfree = (gf_stat->bfree);
    stat->f_bavail = (gf_stat->bavail);
    stat->f_files = (gf_stat->files);
    stat->f_ffree = (gf_stat->ffree);
    stat->f_favail = (gf_stat->favail);
    stat->f_fsid = (gf_stat->fsid);
    stat->f_flag = (gf_stat->flag);
    stat->f_namemax = (gf_stat->namemax);
}

static inline void
gf_statfs_from_statfs(struct gf_statfs *gf_stat, struct statvfs *stat)
{
    if (!stat || !gf_stat)
        return;

    gf_stat->bsize = stat->f_bsize;
    gf_stat->frsize = stat->f_frsize;
    gf_stat->blocks = stat->f_blocks;
    gf_stat->bfree = stat->f_bfree;
    gf_stat->bavail = stat->f_bavail;
    gf_stat->files = stat->f_files;
    gf_stat->ffree = stat->f_ffree;
    gf_stat->favail = stat->f_favail;
    gf_stat->fsid = stat->f_fsid;
    gf_stat->flag = stat->f_flag;
    gf_stat->namemax = stat->f_namemax;
}

static inline void
gf_proto_lease_to_lease(struct gf_proto_lease *gf_proto_lease,
                        struct gf_lease *gf_lease)
{
    if (!gf_lease || !gf_proto_lease)
        return;

    gf_lease->cmd = gf_proto_lease->cmd;
    gf_lease->lease_type = gf_proto_lease->lease_type;
    memcpy(gf_lease->lease_id, gf_proto_lease->lease_id, LEASE_ID_SIZE);
}

static inline void
gf_proto_lease_from_lease(struct gf_proto_lease *gf_proto_lease,
                          struct gf_lease *gf_lease)
{
    if (!gf_lease || !gf_proto_lease)
        return;

    gf_proto_lease->cmd = gf_lease->cmd;
    gf_proto_lease->lease_type = gf_lease->lease_type;
    memcpy(gf_proto_lease->lease_id, gf_lease->lease_id, LEASE_ID_SIZE);
}

static inline int
gf_proto_recall_lease_to_upcall(xlator_t *this,
                                struct gfs3_recall_lease_req *recall_lease,
                                struct gf_upcall *gf_up_data)
{
    struct gf_upcall_recall_lease *tmp = NULL;
    int ret = 0;

    GF_VALIDATE_OR_GOTO(this->name, recall_lease, out);
    GF_VALIDATE_OR_GOTO(this->name, gf_up_data, out);

    tmp = (struct gf_upcall_recall_lease *)gf_up_data->data;
    tmp->lease_type = recall_lease->lease_type;
    memcpy(gf_up_data->gfid, recall_lease->gfid, 16);
    memcpy(tmp->tid, recall_lease->tid, 16);

    GF_PROTOCOL_DICT_UNSERIALIZE(
        this, tmp->dict, (recall_lease->xdata).xdata_val,
        (recall_lease->xdata).xdata_len, ret, errno, out);
out:
    return ret;
}

static inline int
gf_proto_recall_lease_from_upcall(xlator_t *this,
                                  struct gfs3_recall_lease_req *recall_lease,
                                  struct gf_upcall *gf_up_data)
{
    struct gf_upcall_recall_lease *tmp = NULL;
    int ret = 0;

    GF_VALIDATE_OR_GOTO(this->name, recall_lease, out);
    GF_VALIDATE_OR_GOTO(this->name, gf_up_data, out);

    tmp = (struct gf_upcall_recall_lease *)gf_up_data->data;
    recall_lease->lease_type = tmp->lease_type;
    memcpy(recall_lease->gfid, gf_up_data->gfid, 16);
    memcpy(recall_lease->tid, tmp->tid, 16);

    GF_PROTOCOL_DICT_SERIALIZE(this, tmp->dict,
                               &(recall_lease->xdata).xdata_val,
                               (recall_lease->xdata).xdata_len, ret, out);
out:
    return ret;
}

static inline void
gf_proto_flock_to_flock(struct gf_proto_flock *gf_proto_flock,
                        struct gf_flock *gf_flock)
{
    if (!gf_flock || !gf_proto_flock)
        return;

    gf_flock->l_type = gf_proto_flock->type;
    gf_flock->l_whence = gf_proto_flock->whence;
    gf_flock->l_start = gf_proto_flock->start;
    gf_flock->l_len = gf_proto_flock->len;
    gf_flock->l_pid = gf_proto_flock->pid;
    gf_flock->l_owner.len = gf_proto_flock->lk_owner.lk_owner_len;
    if (gf_flock->l_owner.len &&
        (gf_flock->l_owner.len < GF_MAX_LOCK_OWNER_LEN))
        memcpy(gf_flock->l_owner.data, gf_proto_flock->lk_owner.lk_owner_val,
               gf_flock->l_owner.len);
}

static inline void
gf_proto_flock_from_flock(struct gf_proto_flock *gf_proto_flock,
                          struct gf_flock *gf_flock)
{
    if (!gf_flock || !gf_proto_flock)
        return;

    gf_proto_flock->type = (gf_flock->l_type);
    gf_proto_flock->whence = (gf_flock->l_whence);
    gf_proto_flock->start = (gf_flock->l_start);
    gf_proto_flock->len = (gf_flock->l_len);
    gf_proto_flock->pid = (gf_flock->l_pid);
    gf_proto_flock->lk_owner.lk_owner_len = gf_flock->l_owner.len;
    if (gf_flock->l_owner.len)
        gf_proto_flock->lk_owner.lk_owner_val = gf_flock->l_owner.data;
}

static inline void
gf_stat_to_iatt(struct gf_iatt *gf_stat, struct iatt *iatt)
{
    if (!iatt || !gf_stat)
        return;

    memcpy(iatt->ia_gfid, gf_stat->ia_gfid, 16);
    iatt->ia_ino = gf_stat->ia_ino;
    iatt->ia_dev = gf_stat->ia_dev;
    iatt->ia_type = ia_type_from_st_mode(gf_stat->mode);
    iatt->ia_prot = ia_prot_from_st_mode(gf_stat->mode);
    iatt->ia_nlink = gf_stat->ia_nlink;
    iatt->ia_uid = gf_stat->ia_uid;
    iatt->ia_gid = gf_stat->ia_gid;
    iatt->ia_rdev = gf_stat->ia_rdev;
    iatt->ia_size = gf_stat->ia_size;
    iatt->ia_blksize = gf_stat->ia_blksize;
    iatt->ia_blocks = gf_stat->ia_blocks;
    iatt->ia_atime = gf_stat->ia_atime;
    iatt->ia_atime_nsec = gf_stat->ia_atime_nsec;
    iatt->ia_mtime = gf_stat->ia_mtime;
    iatt->ia_mtime_nsec = gf_stat->ia_mtime_nsec;
    iatt->ia_ctime = gf_stat->ia_ctime;
    iatt->ia_ctime_nsec = gf_stat->ia_ctime_nsec;
}

static inline void
gf_stat_from_iatt(struct gf_iatt *gf_stat, struct iatt *iatt)
{
    if (!iatt || !gf_stat)
        return;

    memcpy(gf_stat->ia_gfid, iatt->ia_gfid, 16);
    gf_stat->ia_ino = iatt->ia_ino;
    gf_stat->ia_dev = iatt->ia_dev;
    gf_stat->mode = st_mode_from_ia(iatt->ia_prot, iatt->ia_type);
    gf_stat->ia_nlink = iatt->ia_nlink;
    gf_stat->ia_uid = iatt->ia_uid;
    gf_stat->ia_gid = iatt->ia_gid;
    gf_stat->ia_rdev = iatt->ia_rdev;
    gf_stat->ia_size = iatt->ia_size;
    gf_stat->ia_blksize = iatt->ia_blksize;
    gf_stat->ia_blocks = iatt->ia_blocks;
    gf_stat->ia_atime = iatt->ia_atime;
    gf_stat->ia_atime_nsec = iatt->ia_atime_nsec;
    gf_stat->ia_mtime = iatt->ia_mtime;
    gf_stat->ia_mtime_nsec = iatt->ia_mtime_nsec;
    gf_stat->ia_ctime = iatt->ia_ctime;
    gf_stat->ia_ctime_nsec = iatt->ia_ctime_nsec;
}

static inline int
gf_proto_cache_invalidation_from_upcall(
    xlator_t *this, gfs3_cbk_cache_invalidation_req *gf_c_req,
    struct gf_upcall *gf_up_data)
{
    struct gf_upcall_cache_invalidation *gf_c_data = NULL;
    int is_cache_inval = 0;
    int ret = -1;

    GF_VALIDATE_OR_GOTO(this->name, gf_c_req, out);
    GF_VALIDATE_OR_GOTO(this->name, gf_up_data, out);

    is_cache_inval = ((gf_up_data->event_type == GF_UPCALL_CACHE_INVALIDATION)
                          ? 1
                          : 0);
    GF_VALIDATE_OR_GOTO(this->name, is_cache_inval, out);

    gf_c_data = (struct gf_upcall_cache_invalidation *)gf_up_data->data;
    GF_VALIDATE_OR_GOTO(this->name, gf_c_data, out);

    gf_c_req->gfid = uuid_utoa(gf_up_data->gfid);
    gf_c_req->event_type = gf_up_data->event_type;
    gf_c_req->flags = gf_c_data->flags;
    gf_c_req->expire_time_attr = gf_c_data->expire_time_attr;
    gf_stat_from_iatt(&gf_c_req->stat, &gf_c_data->stat);
    gf_stat_from_iatt(&gf_c_req->parent_stat, &gf_c_data->p_stat);
    gf_stat_from_iatt(&gf_c_req->oldparent_stat, &gf_c_data->oldp_stat);

    ret = 0;
    GF_PROTOCOL_DICT_SERIALIZE(this, gf_c_data->dict,
                               &(gf_c_req->xdata).xdata_val,
                               (gf_c_req->xdata).xdata_len, ret, out);
out:
    return ret;
}

static inline int
gf_proto_cache_invalidation_to_upcall(xlator_t *this,
                                      gfs3_cbk_cache_invalidation_req *gf_c_req,
                                      struct gf_upcall *gf_up_data)
{
    struct gf_upcall_cache_invalidation *gf_c_data = NULL;
    int ret = -1;

    GF_VALIDATE_OR_GOTO(this->name, gf_c_req, out);
    GF_VALIDATE_OR_GOTO(this->name, gf_up_data, out);

    gf_c_data = (struct gf_upcall_cache_invalidation *)gf_up_data->data;
    GF_VALIDATE_OR_GOTO(this->name, gf_c_data, out);

    ret = gf_uuid_parse(gf_c_req->gfid, gf_up_data->gfid);
    if (ret) {
        gf_log(this->name, GF_LOG_WARNING, "gf_uuid_parse(%s) failed",
               gf_c_req->gfid);
        gf_up_data->event_type = GF_UPCALL_EVENT_NULL;
        goto out;
    }

    gf_up_data->event_type = gf_c_req->event_type;

    gf_c_data->flags = gf_c_req->flags;
    gf_c_data->expire_time_attr = gf_c_req->expire_time_attr;
    gf_stat_to_iatt(&gf_c_req->stat, &gf_c_data->stat);
    gf_stat_to_iatt(&gf_c_req->parent_stat, &gf_c_data->p_stat);
    gf_stat_to_iatt(&gf_c_req->oldparent_stat, &gf_c_data->oldp_stat);

    ret = 0;
    GF_PROTOCOL_DICT_UNSERIALIZE(this, gf_c_data->dict,
                                 (gf_c_req->xdata).xdata_val,
                                 (gf_c_req->xdata).xdata_len, ret, ret, out);

    /* If no dict was sent, create an empty dict, so that each xlator
     * need not check if empty then create new dict. Will be unref'd by the
     * caller */
    if (!gf_c_data->dict)
        gf_c_data->dict = dict_new();
out:
    return ret;
}

static inline int
gf_proto_inodelk_contention_to_upcall(xlator_t *this,
                                      struct gfs4_inodelk_contention_req *lc,
                                      struct gf_upcall *gf_up_data)
{
    struct gf_upcall_inodelk_contention *tmp = NULL;
    int ret = -1;
    int op_errno = EINVAL;

    GF_VALIDATE_OR_GOTO(this->name, lc, out);
    GF_VALIDATE_OR_GOTO(this->name, gf_up_data, out);

    tmp = (struct gf_upcall_inodelk_contention *)gf_up_data->data;

    gf_uuid_copy(gf_up_data->gfid, (unsigned char *)lc->gfid);

    gf_proto_flock_to_flock(&lc->flock, &tmp->flock);
    tmp->pid = lc->pid;
    tmp->domain = lc->domain;
    if ((tmp->domain != NULL) && (*tmp->domain == 0)) {
        tmp->domain = NULL;
    }

    GF_PROTOCOL_DICT_UNSERIALIZE(this, tmp->xdata, lc->xdata.xdata_val,
                                 lc->xdata.xdata_len, ret, op_errno, out);

    ret = 0;

out:
    if (ret < 0) {
        ret = -op_errno;
    }

    return ret;
}

static inline int
gf_proto_inodelk_contention_from_upcall(xlator_t *this,
                                        struct gfs4_inodelk_contention_req *lc,
                                        struct gf_upcall *gf_up_data)
{
    struct gf_upcall_inodelk_contention *tmp = NULL;
    int ret = -1;
    int op_errno = EINVAL;

    GF_VALIDATE_OR_GOTO(this->name, lc, out);
    GF_VALIDATE_OR_GOTO(this->name, gf_up_data, out);

    tmp = (struct gf_upcall_inodelk_contention *)gf_up_data->data;

    gf_uuid_copy((unsigned char *)lc->gfid, gf_up_data->gfid);

    gf_proto_flock_from_flock(&lc->flock, &tmp->flock);
    lc->pid = tmp->pid;
    lc->domain = (char *)tmp->domain;
    if (lc->domain == NULL) {
        lc->domain = "";
    }

    GF_PROTOCOL_DICT_SERIALIZE(this, tmp->xdata, &lc->xdata.xdata_val,
                               lc->xdata.xdata_len, op_errno, out);

    ret = 0;

out:
    if (ret < 0) {
        ret = -op_errno;
    }

    return ret;
}

static inline int
gf_proto_entrylk_contention_to_upcall(xlator_t *this,
                                      struct gfs4_entrylk_contention_req *lc,
                                      struct gf_upcall *gf_up_data)
{
    struct gf_upcall_entrylk_contention *tmp = NULL;
    int ret = -1;
    int op_errno = EINVAL;

    GF_VALIDATE_OR_GOTO(this->name, lc, out);
    GF_VALIDATE_OR_GOTO(this->name, gf_up_data, out);

    tmp = (struct gf_upcall_entrylk_contention *)gf_up_data->data;

    gf_uuid_copy(gf_up_data->gfid, (unsigned char *)lc->gfid);

    tmp->type = lc->type;
    tmp->name = lc->name;
    if ((tmp->name != NULL) && (*tmp->name == 0)) {
        tmp->name = NULL;
    }
    tmp->pid = lc->pid;
    tmp->domain = lc->domain;
    if ((tmp->domain != NULL) && (*tmp->domain == 0)) {
        tmp->domain = NULL;
    }

    GF_PROTOCOL_DICT_UNSERIALIZE(this, tmp->xdata, lc->xdata.xdata_val,
                                 lc->xdata.xdata_len, ret, op_errno, out);

    ret = 0;

out:
    if (ret < 0) {
        ret = -op_errno;
    }

    return ret;
}

static inline int
gf_proto_entrylk_contention_from_upcall(xlator_t *this,
                                        struct gfs4_entrylk_contention_req *lc,
                                        struct gf_upcall *gf_up_data)
{
    struct gf_upcall_entrylk_contention *tmp = NULL;
    int ret = -1;
    int op_errno = EINVAL;

    GF_VALIDATE_OR_GOTO(this->name, lc, out);
    GF_VALIDATE_OR_GOTO(this->name, gf_up_data, out);

    tmp = (struct gf_upcall_entrylk_contention *)gf_up_data->data;

    gf_uuid_copy((unsigned char *)lc->gfid, gf_up_data->gfid);

    lc->type = tmp->type;
    lc->name = (char *)tmp->name;
    if (lc->name == NULL) {
        lc->name = "";
    }
    lc->pid = tmp->pid;
    lc->domain = (char *)tmp->domain;
    if (lc->domain == NULL) {
        lc->domain = "";
    }

    GF_PROTOCOL_DICT_SERIALIZE(this, tmp->xdata, &lc->xdata.xdata_val,
                               lc->xdata.xdata_len, op_errno, out);

    ret = 0;

out:
    if (ret < 0) {
        ret = -op_errno;
    }

    return ret;
}

static inline void
gfx_mdata_iatt_to_mdata_iatt(struct gfx_mdata_iatt *gf_mdata_iatt,
                             struct mdata_iatt *mdata_iatt)
{
    if (!mdata_iatt || !gf_mdata_iatt)
        return;
    mdata_iatt->ia_atime = gf_mdata_iatt->ia_atime;
    mdata_iatt->ia_atime_nsec = gf_mdata_iatt->ia_atime_nsec;
    mdata_iatt->ia_mtime = gf_mdata_iatt->ia_mtime;
    mdata_iatt->ia_mtime_nsec = gf_mdata_iatt->ia_mtime_nsec;
    mdata_iatt->ia_ctime = gf_mdata_iatt->ia_ctime;
    mdata_iatt->ia_ctime_nsec = gf_mdata_iatt->ia_ctime_nsec;
}

static inline void
gfx_mdata_iatt_from_mdata_iatt(struct gfx_mdata_iatt *gf_mdata_iatt,
                               struct mdata_iatt *mdata_iatt)
{
    if (!mdata_iatt || !gf_mdata_iatt)
        return;
    gf_mdata_iatt->ia_atime = mdata_iatt->ia_atime;
    gf_mdata_iatt->ia_atime_nsec = mdata_iatt->ia_atime_nsec;
    gf_mdata_iatt->ia_mtime = mdata_iatt->ia_mtime;
    gf_mdata_iatt->ia_mtime_nsec = mdata_iatt->ia_mtime_nsec;
    gf_mdata_iatt->ia_ctime = mdata_iatt->ia_ctime;
    gf_mdata_iatt->ia_ctime_nsec = mdata_iatt->ia_ctime_nsec;
}

static inline void
gfx_stat_to_iattx(struct gfx_iattx *gf_stat, struct iatt *iatt)
{
    if (!iatt || !gf_stat)
        return;

    memcpy(iatt->ia_gfid, gf_stat->ia_gfid, 16);

    iatt->ia_flags = gf_stat->ia_flags;
    iatt->ia_ino = gf_stat->ia_ino;
    iatt->ia_dev = gf_stat->ia_dev;
    iatt->ia_rdev = gf_stat->ia_rdev;
    iatt->ia_size = gf_stat->ia_size;
    iatt->ia_nlink = gf_stat->ia_nlink;
    iatt->ia_uid = gf_stat->ia_uid;
    iatt->ia_gid = gf_stat->ia_gid;
    iatt->ia_blksize = gf_stat->ia_blksize;
    iatt->ia_blocks = gf_stat->ia_blocks;
    iatt->ia_atime = gf_stat->ia_atime;
    iatt->ia_atime_nsec = gf_stat->ia_atime_nsec;
    iatt->ia_mtime = gf_stat->ia_mtime;
    iatt->ia_mtime_nsec = gf_stat->ia_mtime_nsec;
    iatt->ia_ctime = gf_stat->ia_ctime;
    iatt->ia_ctime_nsec = gf_stat->ia_ctime_nsec;
    iatt->ia_btime = gf_stat->ia_btime;
    iatt->ia_btime_nsec = gf_stat->ia_btime_nsec;
    iatt->ia_attributes = gf_stat->ia_attributes;
    iatt->ia_attributes_mask = gf_stat->ia_attributes_mask;

    iatt->ia_type = ia_type_from_st_mode(gf_stat->mode);
    iatt->ia_prot = ia_prot_from_st_mode(gf_stat->mode);
}

static inline void
gfx_stat_from_iattx(struct gfx_iattx *gf_stat, struct iatt *iatt)
{
    if (!iatt || !gf_stat)
        return;

    memcpy(gf_stat->ia_gfid, iatt->ia_gfid, 16);
    gf_stat->ia_ino = iatt->ia_ino;
    gf_stat->ia_dev = iatt->ia_dev;

    gf_stat->ia_nlink = iatt->ia_nlink;
    gf_stat->ia_uid = iatt->ia_uid;
    gf_stat->ia_gid = iatt->ia_gid;
    gf_stat->ia_rdev = iatt->ia_rdev;
    gf_stat->ia_size = iatt->ia_size;
    gf_stat->ia_blksize = iatt->ia_blksize;
    gf_stat->ia_blocks = iatt->ia_blocks;
    gf_stat->ia_atime = iatt->ia_atime;
    gf_stat->ia_atime_nsec = iatt->ia_atime_nsec;
    gf_stat->ia_mtime = iatt->ia_mtime;
    gf_stat->ia_mtime_nsec = iatt->ia_mtime_nsec;
    gf_stat->ia_ctime = iatt->ia_ctime;
    gf_stat->ia_ctime_nsec = iatt->ia_ctime_nsec;

    gf_stat->ia_flags = iatt->ia_flags;
    gf_stat->ia_btime = iatt->ia_btime;
    gf_stat->ia_btime_nsec = iatt->ia_btime_nsec;
    gf_stat->ia_attributes = iatt->ia_attributes;
    gf_stat->ia_attributes_mask = iatt->ia_attributes_mask;

    gf_stat->mode = st_mode_from_ia(iatt->ia_prot, iatt->ia_type);
}

/* dict_to_xdr () */
static inline int
dict_to_xdr(dict_t *this, gfx_dict *dict)
{
    int ret = -1;
    int i = 0;
    int index = 0;
    data_pair_t *dpair = NULL;
    gfx_dict_pair *xpair = NULL;
    ssize_t size = 0;

    /* This is a failure as we expect destination to be valid */
    if (!dict)
        goto out;

    /* This is OK as dictionary can be null, in which case, destination
       should also know that it is NULL. */
    if (!this) {
        /* encode special meaning data here,
           while decoding, you know it is NULL dict */
        dict->count = -1;
        /* everything else is normal */
        dict->pairs.pairs_len = 0;
        ret = 0;
        goto out;
    }

    /* Do the whole operation in locked region */
    LOCK(&this->lock);

    dict->pairs.pairs_val = GF_CALLOC(1, (this->count * sizeof(gfx_dict_pair)),
                                      gf_common_mt_char);
    if (!dict->pairs.pairs_val)
        goto out;

    dpair = this->members_list;
    for (i = 0; i < this->count; i++) {
        xpair = &dict->pairs.pairs_val[index];

        xpair->key.key_val = dpair->key;
        xpair->key.key_len = strlen(dpair->key) + 1;
        xpair->value.type = dpair->value->data_type;
        switch (dpair->value->data_type) {
                /* Add more type here */
            case GF_DATA_TYPE_INT:
                index++;
                xpair->value.gfx_value_u.value_int = strtoll(dpair->value->data,
                                                             NULL, 0);
                break;
            case GF_DATA_TYPE_UINT:
                index++;
                xpair->value.gfx_value_u.value_uint = strtoull(
                    dpair->value->data, NULL, 0);
                break;
            case GF_DATA_TYPE_DOUBLE:
                index++;
                xpair->value.gfx_value_u.value_dbl = strtod(dpair->value->data,
                                                            NULL);
                break;
            case GF_DATA_TYPE_STR:
                index++;
                xpair->value.gfx_value_u.val_string
                    .val_string_val = dpair->value->data;
                xpair->value.gfx_value_u.val_string
                    .val_string_len = dpair->value->len;
                break;
            case GF_DATA_TYPE_IATT:
                index++;
                gfx_stat_from_iattx(&xpair->value.gfx_value_u.iatt,
                                    (struct iatt *)dpair->value->data);
                break;
            case GF_DATA_TYPE_MDATA:
                index++;
                gfx_mdata_iatt_from_mdata_iatt(
                    &xpair->value.gfx_value_u.mdata_iatt,
                    (struct mdata_iatt *)dpair->value->data);
                break;
            case GF_DATA_TYPE_GFUUID:
                index++;
                memcpy(&xpair->value.gfx_value_u.uuid, dpair->value->data,
                       sizeof(uuid_t));
                break;

            case GF_DATA_TYPE_PTR:
            case GF_DATA_TYPE_STR_OLD:
                index++;
                /* Ideally, each type of data stored in dictionary
                   should have type. A pointer type shouldn't be
                   sent on wire */

                /* This is done for backward compatibility as dict is
                   heavily used for transporting data over wire.
                   Ideally, wherever there is an issue, fix and
                   move on */
                xpair->value.gfx_value_u.other.other_val = dpair->value->data;
                xpair->value.gfx_value_u.other.other_len = dpair->value->len;

                /* Change this to INFO, after taking the above down */
                gf_msg("dict", GF_LOG_DEBUG, EINVAL, LG_MSG_DICT_SERIAL_FAILED,
                       "key '%s' would not be sent on wire in the future",
                       dpair->key);
                break;
            default:
                /* Unknown type and ptr type is not sent on wire */
                gf_msg("dict", GF_LOG_WARNING, EINVAL,
                       LG_MSG_DICT_SERIAL_FAILED,
                       "key '%s' is not sent on wire", dpair->key);
                break;
        }
        dpair = dpair->next;
    }

    dict->pairs.pairs_len = index;
    dict->count = index;

    /* This is required mainly in the RPC layer to understand the
       boundary for proper payload. Hence only send the size of
       variable XDR size. ie, the formula should be:
       xdr_size = total size - (xdr_size + count + pairs.pairs_len))  */
    size = xdr_sizeof((xdrproc_t)xdr_gfx_dict, dict);

    dict->xdr_size = (size > 12) ? (size - 12) : 0;

    ret = 0;
out:
    /* this can be null here, so unlock only if its not null */
    if (this)
        UNLOCK(&this->lock);

    return ret;
}

static inline int
xdr_to_dict(gfx_dict *dict, dict_t **to)
{
    int ret = -1;
    int index = 0;
    char *key = NULL;
    char *value = NULL;
    gfx_dict_pair *xpair = NULL;
    dict_t *this = NULL;
    unsigned char *uuid = NULL;
    struct iatt *iatt = NULL;
    struct mdata_iatt *mdata_iatt = NULL;

    if (!to || !dict)
        goto out;

    if (dict->count < 0) {
        /* indicates NULL dict was passed for encoding */
        ret = 0;
        goto out;
    }

    this = dict_new();
    if (!this)
        goto out;

    for (index = 0; index < dict->pairs.pairs_len; index++) {
        ret = -1;
        xpair = &dict->pairs.pairs_val[index];

        key = xpair->key.key_val;
        switch (xpair->value.type) {
                /* Add more type here */
            case GF_DATA_TYPE_INT:
                ret = dict_set_int64(this, key,
                                     xpair->value.gfx_value_u.value_int);
                break;
            case GF_DATA_TYPE_UINT:
                ret = dict_set_uint64(this, key,
                                      xpair->value.gfx_value_u.value_uint);
                break;
            case GF_DATA_TYPE_DOUBLE:
                ret = dict_set_double(this, key,
                                      xpair->value.gfx_value_u.value_dbl);
                break;
            case GF_DATA_TYPE_STR:
                value = GF_MALLOC(
                    xpair->value.gfx_value_u.val_string.val_string_len + 1,
                    gf_common_mt_char);
                if (!value) {
                    errno = ENOMEM;
                    goto out;
                }
                memcpy(value,
                       xpair->value.gfx_value_u.val_string.val_string_val,
                       xpair->value.gfx_value_u.val_string.val_string_len);
                value[xpair->value.gfx_value_u.val_string.val_string_len] =
                    '\0';
                free(xpair->value.gfx_value_u.val_string.val_string_val);
                ret = dict_set_dynstr(this, key, value);
                break;
            case GF_DATA_TYPE_GFUUID:
                uuid = GF_MALLOC(sizeof(uuid_t), gf_common_mt_uuid_t);
                if (!uuid) {
                    errno = ENOMEM;
                    goto out;
                }
                memcpy(uuid, xpair->value.gfx_value_u.uuid, sizeof(uuid_t));
                ret = dict_set_gfuuid(this, key, uuid, false);
                break;
            case GF_DATA_TYPE_IATT:
                iatt = GF_CALLOC(1, sizeof(struct iatt), gf_common_mt_char);
                if (!iatt) {
                    errno = ENOMEM;
                    goto out;
                }
                gfx_stat_to_iattx(&xpair->value.gfx_value_u.iatt, iatt);
                ret = dict_set_iatt(this, key, iatt, false);
                break;
            case GF_DATA_TYPE_MDATA:
                mdata_iatt = GF_CALLOC(1, sizeof(struct mdata_iatt),
                                       gf_common_mt_char);
                if (!mdata_iatt) {
                    errno = ENOMEM;
                    gf_msg(THIS->name, GF_LOG_ERROR, ENOMEM, LG_MSG_NO_MEMORY,
                           "failed to allocate memory. key: %s", key);
                    ret = -1;
                    goto out;
                }
                gfx_mdata_iatt_to_mdata_iatt(
                    &xpair->value.gfx_value_u.mdata_iatt, mdata_iatt);
                ret = dict_set_mdata(this, key, mdata_iatt, false);
                if (ret != 0) {
                    GF_FREE(mdata_iatt);
                    gf_msg(THIS->name, GF_LOG_ERROR, ENOMEM,
                           LG_MSG_DICT_SET_FAILED,
                           "failed to set the key (%s)"
                           " into dict",
                           key);
                    ret = -1;
                    goto out;
                }
                break;
            case GF_DATA_TYPE_PTR:
            case GF_DATA_TYPE_STR_OLD:
                value = GF_MALLOC(xpair->value.gfx_value_u.other.other_len + 1,
                                  gf_common_mt_char);
                if (!value) {
                    errno = ENOMEM;
                    goto out;
                }
                memcpy(value, xpair->value.gfx_value_u.other.other_val,
                       xpair->value.gfx_value_u.other.other_len);
                value[xpair->value.gfx_value_u.other.other_len] = '\0';
                free(xpair->value.gfx_value_u.other.other_val);
                ret = dict_set_dynptr(this, key, value,
                                      xpair->value.gfx_value_u.other.other_len);
                break;
            default:
                ret = 0;
                /* Unknown type and ptr type is not sent on wire */
                break;
        }
        if (ret) {
            gf_msg_debug(THIS->name, ENOMEM,
                         "failed to set the key (%s) into dict", key);
        }
        free(xpair->key.key_val);
    }

    free(dict->pairs.pairs_val);
    ret = 0;

    /* If everything is fine, assign the dictionary to target */
    *to = this;
    this = NULL;

out:
    if (this)
        dict_unref(this);

    return ret;
}

#endif /* !_GLUSTERFS3_H */
