/*
   Copyright (c) 2011-2017 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 _POSIX_HANDLE_H
#define _POSIX_HANDLE_H

#include "posix-inode-handle.h"

#define HANDLE_ABSPATH_LEN(this)                                               \
    (POSIX_BASE_PATH_LEN(this) +                                               \
     SLEN("/" GF_HIDDEN_PATH "/00/00/" UUID0_STR) + 1)

#define MAKE_PGFID_XATTR_KEY(var, prefix, pgfid)                               \
    do {                                                                       \
        var = alloca(SLEN(prefix) + UUID_CANONICAL_FORM_LEN + 1);              \
        strcpy(var, prefix);                                                   \
        strcat(var, uuid_utoa(pgfid));                                         \
    } while (0)

#define SET_PGFID_XATTR(path, key, value, flags, op_ret, this, label)          \
    do {                                                                       \
        value = hton32(value);                                                 \
        op_ret = sys_lsetxattr(path, key, &value, sizeof(value), flags);       \
        if (op_ret == -1) {                                                    \
            op_errno = errno;                                                  \
            gf_msg(this->name, GF_LOG_WARNING, errno, P_MSG_PGFID_OP,          \
                   "setting xattr failed on %s: key = %s ", path, key);        \
            goto label;                                                        \
        }                                                                      \
    } while (0)

#define SET_PGFID_XATTR_IF_ABSENT(path, key, value, flags, op_ret, this,       \
                                  label)                                       \
    do {                                                                       \
        op_ret = sys_lgetxattr(path, key, &value, sizeof(value));              \
        if (op_ret == -1) {                                                    \
            op_errno = errno;                                                  \
            if (op_errno == ENOATTR) {                                         \
                value = 1;                                                     \
                SET_PGFID_XATTR(path, key, value, flags, op_ret, this, label); \
            } else {                                                           \
                gf_msg(this->name, GF_LOG_WARNING, op_errno, P_MSG_PGFID_OP,   \
                       "getting xattr "                                        \
                       "failed on %s: key = %s ",                              \
                       path, key);                                             \
            }                                                                  \
        }                                                                      \
    } while (0)

#define REMOVE_PGFID_XATTR(path, key, op_ret, this, label)                     \
    do {                                                                       \
        op_ret = sys_lremovexattr(path, key);                                  \
        if (op_ret == -1) {                                                    \
            op_errno = errno;                                                  \
            gf_msg(this->name, GF_LOG_WARNING, op_errno, P_MSG_PGFID_OP,       \
                   "removing xattr failed"                                     \
                   "on %s: key = %s",                                          \
                   path, key);                                                 \
            goto label;                                                        \
        }                                                                      \
    } while (0)

/* should be invoked holding a lock */
#define LINK_MODIFY_PGFID_XATTR(path, key, value, flags, op_ret, this, label)  \
    do {                                                                       \
        op_ret = sys_lgetxattr(path, key, &value, sizeof(value));              \
        if (op_ret == -1) {                                                    \
            op_errno = errno;                                                  \
            if (op_errno == ENOATTR || op_errno == ENODATA) {                  \
                value = 1;                                                     \
            } else {                                                           \
                gf_msg(this->name, GF_LOG_WARNING, errno, P_MSG_PGFID_OP,      \
                       "getting xattr "                                        \
                       "failed on %s: key = %s ",                              \
                       path, key);                                             \
                goto label;                                                    \
            }                                                                  \
        } else {                                                               \
            value = ntoh32(value);                                             \
            value++;                                                           \
        }                                                                      \
        SET_PGFID_XATTR(path, key, value, flags, op_ret, this, label);         \
    } while (0)

/* should be invoked holding a lock */
#define UNLINK_MODIFY_PGFID_XATTR(path, key, value, flags, op_ret, this,       \
                                  label)                                       \
    do {                                                                       \
        op_ret = sys_lgetxattr(path, key, &value, sizeof(value));              \
        if (op_ret == -1) {                                                    \
            op_errno = errno;                                                  \
            gf_msg(this->name, GF_LOG_WARNING, errno, P_MSG_PGFID_OP,          \
                   "getting xattr failed on "                                  \
                   "%s: key = %s ",                                            \
                   path, key);                                                 \
            goto label;                                                        \
        } else {                                                               \
            value = ntoh32(value);                                             \
            value--;                                                           \
            if (value > 0) {                                                   \
                SET_PGFID_XATTR(path, key, value, flags, op_ret, this, label); \
            } else {                                                           \
                REMOVE_PGFID_XATTR(path, key, op_ret, this, label);            \
            }                                                                  \
        }                                                                      \
    } while (0)

#define MAKE_HANDLE_GFID_PATH(var, this, gfid)                                 \
    do {                                                                       \
        int __len = 0;                                                         \
        struct posix_private *__priv = this->private;                          \
        __len = POSIX_GFID_HANDLE_SIZE(__priv->base_path_length);              \
        __len += 256;                                                          \
        var = alloca(__len);                                                   \
        __len = posix_handle_gfid_path(this, gfid, var, __len);                \
    } while (0)

#define MAKE_HANDLE_RELPATH(var, this, gfid, base)                             \
    do {                                                                       \
        int __len;                                                             \
        __len = POSIX_GFID_HANDLE_RELSIZE;                                     \
        if (base) {                                                            \
            __len += (strlen(base) + 1);                                       \
        }                                                                      \
        var = alloca(__len);                                                   \
        __len = posix_handle_relpath(this, gfid, base, var, __len);            \
    } while (0)

#define MAKE_HANDLE_ABSPATH(var, this, gfid)                                   \
    do {                                                                       \
        struct posix_private *__priv = this->private;                          \
        int __len = HANDLE_ABSPATH_LEN(this);                                  \
        var = alloca(__len);                                                   \
        snprintf(var, __len, "%s/" GF_HIDDEN_PATH "/%02x/%02x/%s",             \
                 __priv->base_path, gfid[0], gfid[1], uuid_utoa(gfid));        \
    } while (0)

#define MAKE_HANDLE_ABSPATH_FD(var, this, gfid, dfd)                           \
    do {                                                                       \
        struct posix_private *__priv = this->private;                          \
        int findex = gfid[0];                                                  \
        int __len = POSIX_GFID_HASH2_LEN;                                      \
        var = alloca(__len);                                                   \
        snprintf(var, __len, "%02x/%s", gfid[1], uuid_utoa(gfid));             \
        dfd = __priv->arrdfd[findex];                                          \
    } while (0)

#define MAKE_ENTRY_HANDLE(entp, parp, this, loc, ent_p)                        \
    do {                                                                       \
        char *__parp;                                                          \
                                                                               \
        if (gf_uuid_is_null(loc->pargfid) || !loc->name) {                     \
            gf_msg(this->name, GF_LOG_ERROR, 0, P_MSG_ENTRY_HANDLE_CREATE,     \
                   "null pargfid/name for path %s", loc->path);                \
            break;                                                             \
        }                                                                      \
                                                                               \
        if (strchr(loc->name, '/')) {                                          \
            gf_msg(this->name, GF_LOG_ERROR, 0, P_MSG_ENTRY_HANDLE_CREATE,     \
                   "'/' in name not allowed: (%s)", loc->name);                \
            op_ret = -1;                                                       \
            break;                                                             \
        }                                                                      \
        if (LOC_HAS_ABSPATH(loc)) {                                            \
            MAKE_REAL_PATH(entp, this, loc->path);                             \
            __parp = strdupa(entp);                                            \
            parp = dirname(__parp);                                            \
            op_ret = posix_pstat(this, loc->inode, NULL, entp, ent_p,          \
                                 _gf_false);                                   \
            break;                                                             \
        }                                                                      \
        errno = 0;                                                             \
        op_ret = posix_istat(this, loc->inode, loc->pargfid, loc->name,        \
                             ent_p);                                           \
        if (errno != ELOOP) {                                                  \
            MAKE_HANDLE_PATH(parp, this, loc->pargfid, NULL);                  \
            MAKE_HANDLE_PATH(entp, this, loc->pargfid, loc->name);             \
            if (!parp || !entp) {                                              \
                gf_msg(this->name, GF_LOG_ERROR, errno,                        \
                       P_MSG_ENTRY_HANDLE_CREATE,                              \
                       "Failed to create entry handle "                        \
                       "for path %s",                                          \
                       loc->path);                                             \
            }                                                                  \
            break;                                                             \
        }                                                                      \
        /* __ret == -1 && errno == ELOOP */                                    \
        /* expand ELOOP */                                                     \
    } while (0)

#define POSIX_GFID_HASH2_LEN 45
int
posix_handle_gfid_path(xlator_t *this, uuid_t gfid, char *buf, size_t len);

int
posix_handle_hard(xlator_t *this, const char *path, uuid_t gfid,
                  struct stat *buf);

int
posix_handle_soft(xlator_t *this, const char *real_path, loc_t *loc,
                  uuid_t gfid, struct stat *buf);

int
posix_handle_unset(xlator_t *this, uuid_t gfid, const char *basename);

int
posix_create_link_if_gfid_exists(xlator_t *this, uuid_t gfid, char *real_path,
                                 inode_table_t *itable);

int
posix_check_internal_writes(xlator_t *this, fd_t *fd, int sysfd, dict_t *xdata);

void
posix_disk_space_check(struct posix_private *priv);

dict_t *
_fill_writev_xdata(fd_t *fd, dict_t *xdata, xlator_t *this, int is_append);
#endif /* !_POSIX_HANDLE_H */
