/******************************************************************************
 * Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
 * lcr licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 *     http://license.coscl.org.cn/MulanPSL
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v1 for more details.
 * Author: wujing
 * Create: 2018-11-08
 * Description: provide container conf function
 ******************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <linux/oom.h>

#include "conf.h"
#include "lcrcontainer.h"
#include "lcrcontainer_extend.h"
#include "error.h"
#include "utils.h"
#include "log.h"
#include "buffer.h"
#include "securec.h"

#define SUB_UID_PATH "/etc/subuid"
#define SUB_GID_PATH "/etc/subgid"
#define ID_MAP_LEN 100

/* files limit checker */
static int files_limit_checker(const char *value)
{
    long long limit = 0;
    int ret = 0;

    ret = util_safe_llong(value, &limit);
    if (ret) {
        ret = -1;
    }

    return ret;
}

/* check console log file */
static int check_console_log_file(const char *value)
{
    int ret = 0;

    if (value == NULL) {
        return -1;
    }

    if (strcmp(value, "none") == 0) {
        ret = 1;
    }

    return ret;
}

/* check console log filesize */
static int check_console_log_filesize(const char *value)
{
    int ret = -1;
    int64_t tmp = 0;
    int64_t min = 4 * SIZE_KB;

    if (value == NULL) {
        return ret;
    }

    if (parse_byte_size_string(value, &tmp) == 0 && tmp >= min) {
        ret = 0;
    }

    return ret;
}

/* check oom score adj */
static int check_oom_score_adj(const char *value)
{
    int ret = -1;
    int tmp = 0;
    int min = OOM_SCORE_ADJ_MIN;
    int max = OOM_SCORE_ADJ_MAX;

    if (value == NULL) {
        return ret;
    }

    if (util_safe_int(value, &tmp) == 0 && tmp >= min && tmp <= max) {
        ret = 0;
    }
    lcr_set_error_message(LCR_ERR_RUNTIME, "Invalid value %s, range for oom score adj is [%d, %d]", value, min, max);
    return ret;
}
/* check console log filerotate */
static int check_console_log_filerotate(const char *value)
{
    int ret = -1;
    unsigned int tmp = 0;

    if (value == NULL) {
        return ret;
    }

    if (util_safe_uint(value, &tmp) == 0) {
        ret = 0;
    }

    return ret;
}

/* check rootfs mount */
static int check_rootfs_mount(const char *value)
{
    if (value == NULL) {
        return -1;
    }

    if (!dir_exists(value)) {
        lcr_set_error_message(LCR_ERR_RUNTIME, "Container rootfs mount path '%s' is not exist", value);
        return -1;
    }

    return 0;
}

static inline bool is_native_umask_normal(const char *value)
{
    return strcmp(value, "normal") == 0;
}

static inline bool is_native_umask_secure(const char *value)
{
    return strcmp(value, "secure") == 0;
}


/* check umask */
static int check_native_umask(const char *value)
{
    if (value == NULL) {
        return -1;
    }

    if (!is_native_umask_normal(value) && !is_native_umask_secure(value)) {
        ERROR("Invalid native umask: %s", value);
        return -1;
    }

    return 0;
}

/* check system container */
static int check_system_container(const char *value)
{
    if (value == NULL) {
        return -1;
    }

    if (strcmp(value, "true") != 0) {
        ERROR("Invalid system container: %s", value);
        return -1;
    }

    return 0;
}

/* check cgroup dir */
static int check_cgroup_dir(const char *value)
{
    if (value == NULL) {
        return -1;
    }
    return 0;
}

static const lcr_annotation_item_t g_require_annotations[] = {
    {
        "files.limit",
        "lxc.cgroup.files.limit",
        files_limit_checker,
    },
    {
        "log.console.file",
        "lxc.console.logfile",
        check_console_log_file,
    },
    {
        "log.console.filesize",
        "lxc.console.size",
        check_console_log_filesize,
    },
    {
        "log.console.filerotate",
        "lxc.console.rotate",
        check_console_log_filerotate,
    },
    {
        "rootfs.mount",
        "lxc.rootfs.mount",
        check_rootfs_mount,
    },
    {
        "cgroup.dir",
        "lxc.cgroup.dir",
        check_cgroup_dir,
    },
    {
        "native.umask",
        "lxc.isulad.umask",
        check_native_umask,
    },
    {
        "system.container",
        "lxc.isulad.systemd",
        check_system_container,
    },
    {
        "proc.oom_score_adj",
        "lxc.proc.oom_score_adj",
        check_oom_score_adj,
    },
};

/* create lcr list node */
struct lcr_list *create_lcr_list_node(const char *key, const char *value)
{
    struct lcr_list *node = NULL;
    lcr_config_item_t *entry = NULL;

    node = util_common_calloc_s(sizeof(*node));
    if (node == NULL) {
        return NULL;
    }
    entry = util_common_calloc_s(sizeof(*entry));
    if (entry == NULL) {
        free(node);
        return NULL;
    }
    entry->name = util_strdup_s(key);

    entry->value = util_strdup_s(value);

    node->elem = entry;
    return node;
}

/* free lcr list node */
void free_lcr_list_node(struct lcr_list *node)
{
    lcr_config_item_t *entry = NULL;

    if (node == NULL) {
        return;
    }

    entry = node->elem;
    if (entry != NULL) {
        free(entry->name);
        free(entry->value);
    }
    free(node->elem);
    node->elem = NULL;
    free(node);
}

/* trans oci hostname */
struct lcr_list *trans_oci_hostname(const char *hostname)
{
    if (hostname == NULL) {
        return NULL;
    }

    return create_lcr_list_node("lxc.uts.name", hostname);
}

static bool valid_sep_len(size_t sep_len, size_t len)
{
    if (sep_len && (sep_len != 1) && (len > SIZE_MAX / sep_len + 1)) {
        return false;
    }

    return true;
}

/* capabilities join */
static char *capabilities_join(const char *sep, const char **parts, size_t len)
{
    char *result = NULL;
    size_t sep_len;
    size_t result_len;
    size_t iter;
    int nret = 0;

    sep_len = strlen(sep);
    if (valid_sep_len(sep_len, len) == false) {
        return NULL;
    }
    result_len = (len - 1) * sep_len;
    /* calculate new string length
     * dont calculate `CAP_`
     */
    for (iter = 0; iter < len; iter++) {
        if (result_len > 4 && (result_len - 4 >= SIZE_MAX - strlen(parts[iter]))) {
            return NULL;
        }
        result_len += strlen(parts[iter]) - 4;
    }

    result = calloc(result_len + 1, 1);
    if (result == NULL) {
        return NULL;
    }

    for (iter = 0; iter < len - 1; iter++) {
        nret = strcat_s(result, result_len + 1, &(parts[iter][4]));
        if (nret != EOK) {
            goto err_out;
        }

        nret = strcat_s(result, result_len + 1, sep);
        if (nret != EOK) {
            goto err_out;
        }
    }
    nret = strcat_s(result, result_len + 1, &(parts[len - 1][4]));
    if (nret != EOK) {
        goto err_out;
    }

    // Lower case
    for (iter = 0; iter < result_len; iter++) {
        if (result[iter] >= 'A' && result[iter] <= 'Z') {
            result[iter] = (char)(result[iter] + 32);
        }
    }

    return result;

err_out:
    free(result);
    return NULL;
}

#define UID_MAX_SIZE 21
/* UID to use within a private user namespace for init */
static int trans_oci_process_init_uid(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    char buf[UID_MAX_SIZE] = { 0 };
    int nret;
    int ret = -1;
    if (proc->user != NULL && proc->user->uid != INVALID_INT) {
        nret = sprintf_s(buf, sizeof(buf), "%u", (unsigned int)proc->user->uid);
        if (nret < 0) {
            goto out;
        }

        node = create_lcr_list_node("lxc.init.uid", buf);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    return ret;
}

/* GID to use within a private user namespace for init */
static int trans_oci_process_init_gid(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    char buf[UID_MAX_SIZE] = { 0 };
    int nret;
    int ret = -1;
    if (proc->user != NULL && proc->user->gid != INVALID_INT) {
        nret = sprintf_s(buf, sizeof(buf), "%u", (unsigned int)proc->user->gid);
        if (nret < 0) {
            goto out;
        }

        node = create_lcr_list_node("lxc.init.gid", buf);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    return ret;
}

/* additional groups for init command */
static int trans_oci_process_init_groups(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int nret;
    size_t i = 0;
    int ret = -1;
    if (proc->user != NULL && proc->user->additional_gids != NULL && proc->user->additional_gids_len > 0) {
        if (proc->user->additional_gids_len > (SIZE_MAX / (LCR_NUMSTRLEN64 + 1))) {
            goto out;
        }

        size_t total_len = (LCR_NUMSTRLEN64 + 1) * proc->user->additional_gids_len;
        char *gids = util_common_calloc_s(total_len);
        if (gids == NULL) {
            goto out;
        }

        nret = sprintf_s(gids, total_len, "%u", (unsigned int)(proc->user->additional_gids[0]));
        if (nret < 0) {
            free(gids);
            goto out;
        }
        for (i = 1; i < proc->user->additional_gids_len; i++) {
            size_t old_len = strlen(gids);
            nret = sprintf_s(gids + old_len, total_len - old_len, " %u",
                             (unsigned int)(proc->user->additional_gids[i]));
            if (nret < 0) {
                free(gids);
                goto out;
            }
        }

        node = create_lcr_list_node("lxc.isulad.init.groups", gids);
        free(gids);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    return ret;
}

/* Sets the command to use as the init system for the containers */
static int trans_oci_process_init_args(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    size_t i = 0;
    int ret = -1;
    for (i = 0; i < proc->args_len; i++) {
        node = create_lcr_list_node("lxc.isulad.init.args", proc->args[i]);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    return ret;
}

/* working directory to use within container */
static int trans_oci_process_init_cwd(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int ret = -1;
    if (proc->cwd != NULL) {
        node = create_lcr_list_node("lxc.init.cwd", proc->cwd);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    return ret;
}

/* trans oci process init */
static int trans_oci_process_init(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    int ret = -1;
    if (trans_oci_process_init_uid(proc, conf)) {
        goto out;
    }

    if (trans_oci_process_init_gid(proc, conf)) {
        goto out;
    }

    if (trans_oci_process_init_groups(proc, conf)) {
        goto out;
    }

    if (trans_oci_process_init_args(proc, conf)) {
        goto out;
    }

    if (trans_oci_process_init_cwd(proc, conf)) {
        goto out;
    }

    ret = 0;
out:
    return ret;
}

/* trans oci process env and cap */
static int trans_oci_process_env_and_cap(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    char *boundings = NULL;
    int ret = -1;
    size_t i;

    for (i = 0; i < proc->env_len; i++) {
        char *replaced = util_string_replace(" ", SPACE_MAGIC_STR, proc->env[i]);
        if (replaced == NULL) {
            ERROR("memory allocation error");
            goto out;
        }
        node = create_lcr_list_node("lxc.environment", replaced);
        free(replaced);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }

    if (proc->capabilities != NULL && proc->capabilities->bounding_len > 0) {
        boundings =
            capabilities_join(" ", (const char **)(proc->capabilities->bounding), proc->capabilities->bounding_len);
        if (boundings == NULL) {
            ERROR("Failed to join bounding capabilities");
            goto out;
        }
        node = create_lcr_list_node("lxc.cap.keep", boundings);
        free(boundings);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    } else {
        node = create_lcr_list_node("lxc.cap.keep", "ISULAD_KEEP_NONE");
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    return ret;
}

/* trans oci process prlimit */
static int trans_oci_process_prlimit(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int ret = -1;
    int nret;
    size_t i;

    for (i = 0; i < proc->rlimits_len; i++) {
        oci_runtime_spec_process_rlimits_element *lr = proc->rlimits[i];
        char buf_key[30] = { 0 };
        char buf_value[60] = { 0 };
        size_t j;
        char *type = util_strdup_s(lr->type);

        // Lower case type,eg. RLIMIT_NOFILE -> RLIMIT_nofile
        for (j = strlen("RLIMIT_"); j < strlen(type); j++) {
            type[j] = (char)tolower(type[j]);
        }

        // Skip `RLIMIT_`
        nret = sprintf_s(buf_key, sizeof(buf_key), "lxc.prlimit.%s", &(type[7]));
        free(type);
        if (nret < 0) {
            goto out;
        }

        // We always use format `soft_limit:hard_limit`
        nret = sprintf_s(buf_value, sizeof(buf_value), "%llu:%llu", (unsigned long long)lr->soft,
                         (unsigned long long)lr->hard);
        if (nret < 0) {
            goto out;
        }

        node = create_lcr_list_node(buf_key, buf_value);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    return ret;
}

/* trans oci process no new privs */
static int trans_oci_process_no_new_privs(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int ret = -1;

    if (proc->no_new_privileges) {
        node = create_lcr_list_node("lxc.no_new_privs", "1");
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    return ret;
}

static int trans_oci_process_apparmor(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int ret = -1;

    if (proc->apparmor_profile != NULL) {
        node = create_lcr_list_node("lxc.aa_profile", proc->apparmor_profile);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }

    ret = 0;
out:
    return ret;
}

static int trans_oci_process_selinux(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int ret = -1;

    if (proc->selinux_label != NULL) {
        node = create_lcr_list_node("lxc.se_context", proc->selinux_label);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }

    ret = 0;
out:
    return ret;
}

/* trans oci process apparmor and selinux */
static int trans_oci_process_apparmor_and_selinux(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
    int ret = -1;

    if (trans_oci_process_apparmor(proc, conf) != 0) {
        goto out;
    }

    if (trans_oci_process_selinux(proc, conf) != 0) {
        goto out;
    }

    ret = 0;
out:
    return ret;
}

/* trans oci process */
struct lcr_list *trans_oci_process(const oci_runtime_spec_process *proc)
{
    struct lcr_list *conf = NULL;

    conf = util_common_calloc_s(sizeof(struct lcr_list));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    if (trans_oci_process_init(proc, conf)) {
        goto out_free;
    }

    if (trans_oci_process_env_and_cap(proc, conf)) {
        goto out_free;
    }

    if (trans_oci_process_prlimit(proc, conf)) {
        goto out_free;
    }

    if (trans_oci_process_no_new_privs(proc, conf)) {
        goto out_free;
    }

    if (trans_oci_process_apparmor_and_selinux(proc, conf)) {
        goto out_free;
    }

    return conf;

out_free:
    lcr_free_config(conf);
    free(conf);
    return NULL;
}

#define APPEND_COMMA_END_SIZE 2
/* trans oci root rootfs */
static int trans_oci_root_rootfs(const oci_runtime_spec_root *root, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int ret = -1;

    if ((root != NULL) && root->path != NULL) {
        if (strcmp(root->path, "/") != 0) {
            node = create_lcr_list_node("lxc.rootfs.path", root->path);
            if (node == NULL) {
                goto out;
            }
            lcr_list_add_tail(conf, node);
        }
    }
    ret = 0;
out:
    return ret;
}

static inline bool is_root_readonly(const oci_runtime_spec_root *root)
{
    return root != NULL && root->readonly;
}

/* trans oci root rootfsoptions */
static int trans_oci_root_rootfs_options(const oci_runtime_spec_root *root, struct lcr_list *conf,
                                         const oci_runtime_config_linux *linux)
{
    struct lcr_list *node = NULL;
    char *value = NULL;
    char *tmpvalue = NULL;
    int ret = -1;
    int nret;

    if (is_root_readonly(root)) {
        value = util_strdup_s("ro");
    }

    if ((linux != NULL) && linux->rootfs_propagation != NULL) {
        if (value != NULL) {
            size_t newsize;
            if (strlen(value) > (SIZE_MAX - strlen(linux->rootfs_propagation)) - APPEND_COMMA_END_SIZE) {
                ERROR("Out of range!");
                goto out;
            }
            newsize = strlen(linux->rootfs_propagation) + strlen(value) + APPEND_COMMA_END_SIZE;
            nret = mem_realloc((void **)&tmpvalue, newsize, value, strlen(value));
            if (nret < 0) {
                ERROR("Out of memory");
                goto out;
            }
            value = tmpvalue;
            nret = sprintf_s(value + strlen(value), newsize - strlen(value), ",%s", linux->rootfs_propagation);
            if (nret < 0) {
                ERROR("Failed to print string");
                goto out;
            }
        } else {
            value = util_strdup_s(linux->rootfs_propagation);
        }
    }

    if (value != NULL) {
        node = create_lcr_list_node("lxc.rootfs.options", value);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    free(value);
    return ret;
}

/* trans oci root */
struct lcr_list *trans_oci_root(const oci_runtime_spec_root *root, const oci_runtime_config_linux *linux)
{
    struct lcr_list *conf = NULL;

    conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    if (trans_oci_root_rootfs(root, conf)) {
        goto out_free;
    }

    if (trans_oci_root_rootfs_options(root, conf, linux)) {
        goto out_free;
    }

    return conf;
out_free:
    lcr_free_config(conf);
    free(conf);

    return NULL;
}

static inline bool is_mount_options_invalid(const defs_mount *mount)
{
    return mount == NULL || mount->type == NULL;
}

static inline bool is_mount_type_bind(const char *type)
{
    return strcmp(type, "bind") == 0;
}

static inline bool is_mount_type_cgroup(const char *type)
{
    return strcmp(type, "cgroup") == 0;
}

static inline bool is_mount_type_sysfs(const char *type)
{
    return strcmp(type, "sysfs") == 0;
}

static inline bool is_mount_type_proc(const char *type)
{
    return strcmp(type, "proc") == 0;
}

/* trans mount to lxc options */
static char *trans_mount_to_lxc_options(const defs_mount *mount)
{
    char *result = NULL;
    char *prefix = NULL;
    char *lxc_options = NULL;
    int rc;
    bool isdir = true;
    struct stat st;

    if (is_mount_options_invalid(mount)) {
        return NULL;
    }

    if (is_mount_type_bind(mount->type)) {
        rc = stat(mount->source, &st);
        if (rc != 0) {
            ERROR("Failed to get stat of %s", mount->source);
            goto free_out;
        }
        isdir = S_ISDIR(st.st_mode);
    }

    lxc_options = isdir ? ",create=dir" : ",create=file";

    prefix = util_string_join(",", (const char **)mount->options, mount->options_len);
    if (prefix == NULL) {
        prefix = util_strdup_s("defaults");
    }

    result = util_string_append(lxc_options, prefix);
    free(prefix);
    return result;

free_out:
    free(prefix);
    free(result);
    return NULL;
}

static bool get_mount_option_ro(const defs_mount *mount)
{
    bool ro = false;

    if ((mount->options != NULL) && mount->options_len) {
        size_t i = 0;
        for (i = 0; i < mount->options_len; i++) {
            if ((mount->options[i] != NULL) && !strcmp(mount->options[i], "ro")) {
                ro = true;
                break;
            }
        }
    }

    return ro;
}

static char *get_mount_readmode_options(const defs_mount *mount, const char *type)
{
    char *options = NULL;
    bool readonly = get_mount_option_ro(mount);

    if (is_mount_type_cgroup(type)) {
        if (readonly) {
            options = util_strdup_s("ro:force");
        } else {
            options = util_strdup_s("rw:force");
        }
    } else {
        if (readonly) {
            options = util_strdup_s("ro");
        } else {
            options = util_strdup_s("rw");
        }
    }

    return options;
}

/* trans mount auto to lxc */
static struct lcr_list *trans_mount_auto_to_lxc(const defs_mount *mount)
{
    struct lcr_list *node = NULL;
    size_t buf_len = 0;
    char *buf = NULL;
    char *options = NULL;
    int ret;
    char *type = NULL;

    if (is_mount_options_invalid(mount)) {
        ERROR("oci container mounts element(type) is empty");
        return NULL;
    }
    type = mount->type;
    if (is_mount_type_sysfs(type)) {
        type = "sys";
    }

    options = get_mount_readmode_options(mount, type);
    if (options == NULL) {
        ERROR("Failed to trans to lxc options");
        goto out_free;
    }

    buf_len = strlen(type) + strlen(options) + 2;
    buf = calloc(buf_len, 1);
    if (buf == NULL) {
        DEBUG("Out of memory");
        goto out_free;
    }

    ret = sprintf_s(buf, buf_len, "%s:%s", type, options);
    if (ret < 0) {
        DEBUG("Failed to print string");
        goto out_free;
    }
    node = create_lcr_list_node("lxc.mount.auto", buf);

out_free:
    free(options);
    free(buf);
    return node;
}

/* trans mount entry to lxc */
static struct lcr_list *trans_mount_entry_to_lxc(const defs_mount *mount)
{
    struct lcr_list *node = NULL;
    size_t buf_len = 0;
    char *buf = NULL;
    char *options = NULL;
    char *replaced_dest = NULL;
    int ret;

    char *replaced_source = util_string_replace(" ", SPACE_MAGIC_STR, mount->source);
    if (replaced_source == NULL) {
        ERROR("memory allocation error");
        goto err_out;
    }
    replaced_dest = util_string_replace(" ", SPACE_MAGIC_STR, mount->destination);
    if (replaced_dest == NULL) {
        ERROR("memory allocation error");
        free(replaced_source);
        goto err_out;
    }

    options = trans_mount_to_lxc_options(mount);
    if (options == NULL) {
        ERROR("Failed to trans to lxc options");
        goto out_free;
    }

    buf_len = strlen(replaced_dest) + strlen(mount->type) + strlen(replaced_source) + strlen(options) + 8;
    buf = calloc(buf_len, 1);
    if (buf == NULL) {
        ERROR("Out of memory");
        goto out_free;
    }

    ret = sprintf_s(buf, buf_len, "%s %s %s %s 0 0", replaced_source, replaced_dest + 1, mount->type, options);
    if (ret < 0) {
        ERROR("Failed to print string");
        goto out_free;
    }
    node = create_lcr_list_node("lxc.mount.entry", buf);

out_free:
    free(options);
    free(buf);
    free(replaced_source);
    free(replaced_dest);
err_out:
    return node;
}

static bool is_system_container(const oci_runtime_spec *container)
{
    size_t i = 0;
    for (i = 0; container->annotations != NULL && i < container->annotations->len; i++) {
        if (strcmp(container->annotations->keys[i], "system.container") == 0) {
            return true;
        }
    }
    return false;
}

static struct lcr_list *trans_oci_mounts_normal(const defs_mount *tmp)
{
    struct lcr_list *node = NULL;
    if (is_mount_type_cgroup(tmp->type) || is_mount_type_proc(tmp->type) || is_mount_type_sysfs(tmp->type)) {
        node = trans_mount_auto_to_lxc(tmp);
    } else {
        node = trans_mount_entry_to_lxc(tmp);
    }

    return node;
}

static struct lcr_list *trans_oci_mounts_system_container(const defs_mount *tmp)
{
    struct lcr_list *node = NULL;
    if (is_mount_type_cgroup(tmp->type) || (is_mount_type_proc(tmp->source) && is_mount_type_proc(tmp->type)) ||
        is_mount_type_sysfs(tmp->type)) {
        node = trans_mount_auto_to_lxc(tmp);
    } else {
        node = trans_mount_entry_to_lxc(tmp);
    }

    return node;
}

static struct lcr_list *trans_oci_mounts_node(const oci_runtime_spec *c, const defs_mount *tmp)
{
    struct lcr_list *node = NULL;
    // system container
    if (is_system_container(c)) {
        node = trans_oci_mounts_system_container(tmp);
    } else {
        node = trans_oci_mounts_normal(tmp);
    }

    return node;
}

static inline bool is_mount_destination_dev(const char *destination)
{
    return destination != NULL && strcmp(destination, "/dev") == 0;
}

static inline bool should_ignore_dev_mount(const defs_mount *tmp, bool system_container)
{
    return system_container && is_mount_destination_dev(tmp->destination);
}

/* trans oci mounts */
struct lcr_list *trans_oci_mounts(const oci_runtime_spec *c)
{
    struct lcr_list *conf = NULL;
    struct lcr_list *node = NULL;
    defs_mount *tmp = NULL;
    size_t i;
    bool system_container = is_system_container(c);

    conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    for (i = 0; i < c->mounts_len; i++) {
        tmp = c->mounts[i];
        if (tmp->type == NULL) {
            goto out_free;
        }

        if (should_ignore_dev_mount(tmp, system_container)) {
            continue;
        }
        node = trans_oci_mounts_node(c, tmp);
        if (node == NULL) {
            goto out_free;
        }
        lcr_list_add_tail(conf, node);
    }

    return conf;
out_free:
    lcr_free_config(conf);
    free(conf);

    return NULL;
}

static int trans_one_oci_id_mapping(struct lcr_list *conf, const char *typ, const defs_id_mapping *id, const char *path)
{
    int nret;
    struct lcr_list *node = NULL;
    char buf_value[300] = { 0 };
    char subid[ID_MAP_LEN] = { 0 };

    nret = sprintf_s(buf_value, sizeof(buf_value), "%s %u %u %u", typ, id->container_id, id->host_id, id->size);
    if (nret < 0) {
        return -1;
    }

    node = create_lcr_list_node("lxc.idmap", buf_value);
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);

    nret = sprintf_s(subid, sizeof(subid), "%u:%u:%u", id->container_id, id->host_id, id->size);
    if (nret < 0) {
        return -1;
    }
    nret = util_atomic_write_file(path, subid);
    if (nret < 0) {
        return -1;
    }
    return 0;
}

static int trans_oci_uid_mapping(struct lcr_list *conf, defs_id_mapping **uid_mappings, size_t uid_mappings_len)
{
    size_t i;

    for (i = 0; uid_mappings != NULL && i < uid_mappings_len; i++) {
        int nret = trans_one_oci_id_mapping(conf, "u", uid_mappings[i], SUB_UID_PATH);
        if (nret < 0) {
            return nret;
        }
    }
    return 0;
}

static int trans_oci_gid_mapping(struct lcr_list *conf, defs_id_mapping **gid_mappings, size_t gid_mappings_len)
{
    size_t i;

    for (i = 0; gid_mappings != NULL && i < gid_mappings_len; i++) {
        int nret = trans_one_oci_id_mapping(conf, "g", gid_mappings[i], SUB_GID_PATH);
        if (nret < 0) {
            return nret;
        }
    }
    return 0;
}

/* trans oci id mapping */
static struct lcr_list *trans_oci_id_mapping(const oci_runtime_config_linux *l)
{
    struct lcr_list *conf = NULL;
    int nret = 0;

    conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    nret = trans_oci_uid_mapping(conf, l->uid_mappings, l->uid_mappings_len);
    if (nret < 0) {
        goto out_free;
    }

    nret = trans_oci_gid_mapping(conf, l->gid_mappings, l->gid_mappings_len);
    if (nret < 0) {
        goto out_free;
    }

    return conf;

out_free:
    lcr_free_config(conf);
    free(conf);

    return NULL;
}

#define WILDCARD (-1LL)

static int trans_conf_int(struct lcr_list *conf, const char *lxc_key, int val)
{
    struct lcr_list *node = NULL;
    char buf_value[300] = { 0 };
    int nret;

    nret = sprintf_s(buf_value, sizeof(buf_value), "%d", val);
    if (nret < 0) {
        return -1;
    }
    node = create_lcr_list_node(lxc_key, buf_value);
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);
    return 0;
}

static int trans_conf_uint32(struct lcr_list *conf, const char *lxc_key, uint32_t val)
{
    struct lcr_list *node = NULL;
    char buf_value[300] = { 0 };
    int nret;

    nret = sprintf_s(buf_value, sizeof(buf_value), "%u", (unsigned int)val);
    if (nret < 0) {
        return -1;
    }
    node = create_lcr_list_node(lxc_key, buf_value);
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);
    return 0;
}

static int trans_conf_int64(struct lcr_list *conf, const char *lxc_key, int64_t val)
{
    struct lcr_list *node = NULL;
    char buf_value[300] = { 0 };
    int nret;

    nret = sprintf_s(buf_value, sizeof(buf_value), "%lld", (long long)val);
    if (nret < 0) {
        return -1;
    }
    node = create_lcr_list_node(lxc_key, buf_value);
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);
    return 0;
}

static int trans_conf_uint64(struct lcr_list *conf, const char *lxc_key, uint64_t val)
{
    struct lcr_list *node = NULL;
    char buf_value[300] = { 0 };
    int nret;

    nret = sprintf_s(buf_value, sizeof(buf_value), "%llu", (unsigned long long)val);
    if (nret < 0) {
        return -1;
    }
    node = create_lcr_list_node(lxc_key, buf_value);
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);
    return 0;
}

/* trans resources mem swap */
static int trans_resources_mem_swap(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;
    int nret;

    if (res->memory->reservation != INVALID_INT) {
        /* set soft limit of memory usage */
        nret = trans_conf_int64(conf, "lxc.cgroup.memory.soft_limit_in_bytes", res->memory->reservation);
        if (nret < 0) {
            goto out;
        }
    }
    if (res->memory->swap != INVALID_INT) {
        /* set limit of memory+swap usage */
        nret = trans_conf_int64(conf, "lxc.cgroup.memory.memsw.limit_in_bytes", res->memory->swap);
        if (nret < 0) {
            goto out;
        }
    }

    if (res->memory->swappiness != INVALID_INT) {
        /* set swappiness parameter of vmscan */
        nret = trans_conf_uint64(conf, "lxc.cgroup.memory.swappiness", res->memory->swappiness);
        if (nret < 0) {
            goto out;
        }
    }
    ret = 0;
out:
    return ret;
}

static int trans_resources_mem_limit(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    if (res->memory->limit != INVALID_INT) {
        /* set limit of memory usage */
        int nret = trans_conf_int64(conf, "lxc.cgroup.memory.limit_in_bytes", res->memory->limit);
        if (nret < 0) {
            return -1;
        }
    }
    return 0;
}

/* trans resources mem kernel */
static int trans_resources_mem_kernel(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;
    int nret;

    if (res->memory->kernel != INVALID_INT) {
        /* set hard limit for kernel memory */
        nret = trans_conf_int64(conf, "lxc.cgroup.memory.kmem.limit_in_bytes", res->memory->kernel);
        if (nret < 0) {
            goto out;
        }
    }
    if (res->memory->kernel_tcp != INVALID_INT) {
        /* set hard limit for tcp buf memory */
        nret = trans_conf_int64(conf, "lxc.cgroup.memory.kmem.tcp.limit_in_bytes", res->memory->kernel_tcp);
        if (nret < 0) {
            goto out;
        }
    }
    ret = 0;
out:
    return ret;
}

static int trans_resources_mem_disable_oom(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    if (res->memory->disable_oom_killer) {
        node = create_lcr_list_node("lxc.cgroup.memory.oom_control", "1");
        if (node == NULL) {
            return -1;
        }
        lcr_list_add_tail(conf, node);
    }
    return 0;
}

/* trans resources memory */
static int trans_resources_memory(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;

    if (res->memory == NULL) {
        return 0;
    }

    if (trans_resources_mem_limit(res, conf) != 0) {
        goto out;
    }

    if (trans_resources_mem_swap(res, conf) != 0) {
        goto out;
    }

    if (trans_resources_mem_kernel(res, conf) != 0) {
        goto out;
    }

    if (trans_resources_mem_disable_oom(res, conf) != 0) {
        goto out;
    }
    ret = 0;
out:
    return ret;
}

static int trans_resources_devices_node(const oci_runtime_defs_linux_device_cgroup *lrd, struct lcr_list *conf,
                                        const char *buf_value)
{
    struct lcr_list *node = NULL;
    int ret = -1;

    if (lrd->allow == true) {
        node = create_lcr_list_node("lxc.cgroup.devices.allow", buf_value);
    } else {
        node = create_lcr_list_node("lxc.cgroup.devices.deny", buf_value);
    }
    if (node == NULL) {
        goto out;
    }
    lcr_list_add_tail(conf, node);

    ret = 0;
out:
    return ret;
}

static int trans_resources_devices_no_match(const oci_runtime_defs_linux_device_cgroup *lrd, char *buf_value,
                                            size_t size)
{
    int ret = 0;
    if (lrd->minor != WILDCARD) {
        ret = sprintf_s(buf_value, size, "%s %lld:%lld %s", lrd->type ? lrd->type : "a", (long long)(lrd->major),
                        (long long)lrd->minor, lrd->access ? lrd->access : "rwm");
    } else {
        ret = sprintf_s(buf_value, size, "%s %lld:* %s", lrd->type ? lrd->type : "a", (long long)(lrd->major),
                        lrd->access ? lrd->access : "rwm");
    }

    return ret;
}

static int trans_resources_devices_match(const oci_runtime_defs_linux_device_cgroup *lrd, char *buf_value, size_t size)
{
    int ret = 0;
    if (lrd->minor != WILDCARD) {
        ret = sprintf_s(buf_value, size, "%s *:%lld %s", lrd->type ? lrd->type : "a", (long long)(lrd->minor),
                        lrd->access ? lrd->access : "rwm");
    } else {
        ret = sprintf_s(buf_value, size, "%s *:* %s", lrd->type ? lrd->type : "a", lrd->access ? lrd->access : "rwm");
    }

    return ret;
}

static int trans_resources_devices_ret(const oci_runtime_defs_linux_device_cgroup *lrd, char *buf_value, size_t size)
{
    int ret = 0;
    if (lrd->major != WILDCARD) {
        ret = trans_resources_devices_no_match(lrd, buf_value, size);
    } else {
        ret = trans_resources_devices_match(lrd, buf_value, size);
    }

    return ret;
}

/* trans resources devices */
static int trans_resources_devices(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;
    size_t i = 0;
    char buf_value[300] = { 0 };

    for (i = 0; i < res->devices_len; i++) {
        oci_runtime_defs_linux_device_cgroup *lrd = res->devices[i];
        if (trans_resources_devices_ret(lrd, buf_value, sizeof(buf_value)) < 0) {
            goto out;
        }

        if (trans_resources_devices_node(lrd, conf, buf_value) < 0) {
            goto out;
        }
    }
    ret = 0;
out:
    return ret;
}

/* trans resources cpu cfs */
static int trans_resources_cpu_cfs(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;

    if (res->cpu->quota != INVALID_INT) {
        if (trans_conf_int64(conf, "lxc.cgroup.cpu.cfs_quota_us", res->cpu->quota) < 0) {
            goto out;
        }
    }
    if (res->cpu->period != INVALID_INT) {
        if (trans_conf_uint64(conf, "lxc.cgroup.cpu.cfs_period_us", res->cpu->period) < 0) {
            goto out;
        }
    }
    ret = 0;
out:
    return ret;
}

/* trans resources cpu rt */
static int trans_resources_cpu_rt(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;

    if (res->cpu->realtime_runtime != INVALID_INT) {
        if (trans_conf_int64(conf, "lxc.cgroup.cpu.rt_runtime_us", res->cpu->realtime_runtime) < 0) {
            goto out;
        }
    }
    if (res->cpu->realtime_period != INVALID_INT) {
        if (trans_conf_uint64(conf, "lxc.cgroup.cpu.rt_period_us", res->cpu->realtime_period) < 0) {
            goto out;
        }
    }
    ret = 0;
out:
    return ret;
}

/* trans resources cpu set */
static int trans_resources_cpu_set(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int ret = -1;

    if (res->cpu->cpus != NULL) {
        node = create_lcr_list_node("lxc.cgroup.cpuset.cpus", res->cpu->cpus);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    if (res->cpu->mems != NULL) {
        node = create_lcr_list_node("lxc.cgroup.cpuset.mems", res->cpu->mems);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }
    ret = 0;
out:
    return ret;
}

/* trans resources cpu shares */
static int trans_resources_cpu_shares(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    if (res->cpu->shares != INVALID_INT) {
        int nret = trans_conf_int64(conf, "lxc.cgroup.cpu.shares", (int64_t)(res->cpu->shares));
        if (nret < 0) {
            return -1;
        }
    }
    return 0;
}

/* trans resources cpu */
static int trans_resources_cpu(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;

    if (res->cpu == NULL) {
        return 0;
    }

    if (trans_resources_cpu_cfs(res, conf)) {
        goto out;
    }

    if (trans_resources_cpu_rt(res, conf)) {
        goto out;
    }

    if (trans_resources_cpu_set(res, conf)) {
        goto out;
    }

    if (trans_resources_cpu_shares(res, conf)) {
        goto out;
    }

    ret = 0;

out:
    return ret;
}

/* trans resources blkio weight */
static int trans_blkio_weight(const oci_runtime_config_linux_resources_block_io *block_io, struct lcr_list *conf)
{
    int ret = -1;

    if (block_io->weight != INVALID_INT) {
        if (trans_conf_int(conf, "lxc.cgroup.blkio.weight", block_io->weight) < 0) {
            goto out;
        }
    }
    if (block_io->leaf_weight != INVALID_INT) {
        if (trans_conf_int(conf, "lxc.cgroup.blkio.leaf_weight", block_io->leaf_weight) < 0) {
            goto out;
        }
    }
    ret = 0;

out:
    return ret;
}

/* trans resources blkio wdevice */
static int trans_blkio_wdevice(const oci_runtime_config_linux_resources_block_io *block_io, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int ret = -1;
    size_t i = 0;
    char buf_value[300] = { 0 };

    for (i = 0; i < block_io->weight_device_len; i++) {
        int nret;
        oci_runtime_defs_linux_block_io_device_weight *wd = block_io->weight_device[i];
        if ((wd != NULL) && wd->weight != INVALID_INT) {
            nret = sprintf_s(buf_value, sizeof(buf_value), "%lld:%lld %d", (long long)(wd->major), (long long)wd->minor,
                             wd->weight);
            if (nret < 0) {
                goto out;
            }

            node = create_lcr_list_node("lxc.cgroup.blkio.weight_device", buf_value);
            if (node == NULL) {
                goto out;
            }
            lcr_list_add_tail(conf, node);
        }
        if ((wd != NULL) && wd->leaf_weight != INVALID_INT) {
            nret = sprintf_s(buf_value, sizeof(buf_value), "%lld:%lld %d", (long long)(wd->major),
                             (long long)(wd->minor), wd->leaf_weight);
            if (nret < 0) {
                goto out;
            }

            node = create_lcr_list_node("lxc.cgroup.blkio.leaf_weight_device", buf_value);
            if (node == NULL) {
                goto out;
            }
            lcr_list_add_tail(conf, node);
        }
    }
    ret = 0;
out:
    return ret;
}

/* trans resources blkio throttle */
static int trans_blkio_throttle(oci_runtime_defs_linux_block_io_device_throttle **throttle, size_t len,
                                const char *lxc_key, struct lcr_list *conf)
{
    struct lcr_list *node = NULL;
    int ret = -1;
    size_t i;

    if ((throttle == NULL) || len == 0) {
        return 0;
    }

    for (i = 0; i < len; i++) {
        if (throttle[i] && throttle[i]->rate != INVALID_INT) {
            int nret;
            char buf_value[300] = { 0x00 };
            nret = sprintf_s(buf_value, sizeof(buf_value), "%lld:%lld %llu", (long long)throttle[i]->major,
                             (long long)(throttle[i]->minor), (unsigned long long)(throttle[i]->rate));
            if (nret < 0) {
                goto out;
            }

            node = create_lcr_list_node(lxc_key, buf_value);
            if (node == NULL) {
                goto out;
            }
            lcr_list_add_tail(conf, node);
        }
    }
    ret = 0;
out:
    return ret;
}

/* trans resources blkio */
static int trans_resources_blkio(const oci_runtime_config_linux_resources_block_io *block_io, struct lcr_list *conf)
{
    int ret = -1;

    if (block_io == NULL) {
        return 0;
    }

    if (trans_blkio_weight(block_io, conf)) {
        goto out;
    }

    if (trans_blkio_wdevice(block_io, conf)) {
        goto out;
    }

    if (trans_blkio_throttle(block_io->throttle_read_bps_device, block_io->throttle_read_bps_device_len,
                             "lxc.cgroup.blkio.throttle.read_bps_device", conf)) {
        goto out;
    }

    if (trans_blkio_throttle(block_io->throttle_write_bps_device, block_io->throttle_write_bps_device_len,
                             "lxc.cgroup.blkio.throttle.write_bps_device", conf)) {
        goto out;
    }

    if (trans_blkio_throttle(block_io->throttle_read_iops_device, block_io->throttle_read_iops_device_len,
                             "lxc.cgroup.blkio.throttle.read_iops_device", conf)) {
        goto out;
    }

    if (trans_blkio_throttle(block_io->throttle_write_iops_device, block_io->throttle_write_iops_device_len,
                             "lxc.cgroup.blkio.throttle.write_iops_device", conf)) {
        goto out;
    }

    ret = 0;
out:
    return ret;
}

/* trans resources hugetlb */
static int trans_resources_hugetlb(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;
    size_t i = 0;
    char buf_key[300] = { 0 };

    for (i = 0; i < res->hugepage_limits_len; i++) {
        oci_runtime_config_linux_resources_hugepage_limits_element *lrhl = res->hugepage_limits[i];
        if (lrhl->page_size != NULL) {
            if (sprintf_s(buf_key, sizeof(buf_key), "lxc.cgroup.hugetlb.%s.limit_in_bytes", lrhl->page_size) < 0) {
                goto out;
            }

            if (trans_conf_uint64(conf, buf_key, lrhl->limit) < 0) {
                return -1;
            }
        }
    }

    ret = 0;
out:
    return ret;
}

/* trans resources network */
static int trans_resources_network(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;
    size_t i = 0;
    char buf_value[300] = { 0 };

    if (!res->network) {
        return 0;
    }

    if (res->network->class_id != INVALID_INT) {
        if (trans_conf_uint32(conf, "lxc.cgroup.net_cls.classid", res->network->class_id) < 0) {
            return -1;
        }
    }

    for (i = 0; i < res->network->priorities_len; i++) {
        oci_runtime_defs_linux_network_interface_priority *lrnp = res->network->priorities[i];
        if ((lrnp != NULL) && lrnp->name != NULL && lrnp->priority != INVALID_INT) {
            if (sprintf_s(buf_value, sizeof(buf_value), "%s %u", lrnp->name, lrnp->priority) < 0) {
                goto out;
            }

            struct lcr_list *node = create_lcr_list_node("lxc.cgroup.net_prio.ifpriomap", buf_value);
            if (node == NULL) {
                goto out;
            }
            lcr_list_add_tail(conf, node);
        }
    }

    ret = 0;
out:
    return ret;
}

/* trans resources pids */
static int trans_resources_pids(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
    int ret = -1;
    char buf_value[300] = { 0 };

    if (res->pids == NULL) {
        return 0;
    }

    if (res->pids->limit != INVALID_INT) {
        int nret;
        if (res->pids->limit == -1) {
            nret = sprintf_s(buf_value, sizeof(buf_value), "max");
        } else {
            nret = sprintf_s(buf_value, sizeof(buf_value), "%lld", (long long)(res->pids->limit));
        }
        if (nret < 0) {
            goto out;
        }

        struct lcr_list *node = create_lcr_list_node("lxc.cgroup.pids.max", buf_value);
        if (node == NULL) {
            goto out;
        }
        lcr_list_add_tail(conf, node);
    }

    ret = 0;
out:
    return ret;
}

/* trans oci resources */
static struct lcr_list *trans_oci_resources(const oci_runtime_config_linux_resources *res)
{
    struct lcr_list *conf = NULL;

    conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    if (trans_resources_devices(res, conf)) {
        goto out_free;
    }

    if (trans_resources_memory(res, conf)) {
        goto out_free;
    }

    if (trans_resources_cpu(res, conf)) {
        goto out_free;
    }

    if (trans_resources_blkio(res->block_io, conf)) {
        goto out_free;
    }

    if (trans_resources_hugetlb(res, conf)) {
        goto out_free;
    }

    if (trans_resources_network(res, conf)) {
        goto out_free;
    }

    if (trans_resources_pids(res, conf)) {
        goto out_free;
    }

    return conf;

out_free:
    lcr_free_config(conf);
    free(conf);

    return NULL;
}

struct namespace_map_def {
    char *ns_name;
    char *lxc_name;
};

static char *trans_oci_namespace_to_lxc(const char *typ)
{
    struct namespace_map_def namespaces_map[] = {
        { "pid", "lxc.namespace.share.pid" },       { "network", "lxc.namespace.share.net" },
        { "ipc", "lxc.namespace.share.ipc" },       { "uts", "lxc.namespace.share.uts" },
        { "mount", "lxc.namespace.share.mnt" },     { "user", "lxc.namespace.share.user" },
        { "cgroup", "lxc.namespace.share.cgroup" }, { NULL, NULL }
    };
    const struct namespace_map_def *p = NULL;

    for (p = namespaces_map; p != NULL && p->ns_name != NULL; p++) {
        if (strcmp(typ, p->ns_name) == 0) {
            return util_strdup_s(p->lxc_name);
        }
    }
    return NULL;
}

/* trans oci namespaces */
static struct lcr_list *trans_oci_namespaces(const oci_runtime_config_linux *l)
{
    struct lcr_list *conf = NULL;
    struct lcr_list *node = NULL;
    size_t i;
    oci_runtime_defs_linux_namespace_reference *ns = NULL;

    conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    for (i = 0; i < l->namespaces_len; i++) {
        char *ns_name = NULL;
        ns = l->namespaces[i];

        if (ns->type == NULL || ns->path == NULL) {
            continue;
        }

        ns_name = trans_oci_namespace_to_lxc(ns->type);
        if (ns_name == NULL) {
            continue;
        }

        node = create_lcr_list_node(ns_name, ns->path);
        free(ns_name);
        if (node == NULL) {
            goto out_free;
        }
        lcr_list_add_tail(conf, node);
    }

    return conf;

out_free:
    lcr_free_config(conf);
    free(conf);

    return NULL;
}

/* trans oci mask ro paths */
static struct lcr_list *trans_oci_mask_ro_paths(const oci_runtime_config_linux *l)
{
    struct lcr_list *conf = NULL;
    struct lcr_list *node = NULL;
    size_t i;
    char *path = NULL;

    conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    for (i = 0; i < l->masked_paths_len; i++) {
        path = l->masked_paths[i];
        if (path == NULL) {
            continue;
        }
        node = create_lcr_list_node("lxc.isulad.rootfs.maskedpaths", path);
        if (node == NULL) {
            goto out_free;
        }
        lcr_list_add_tail(conf, node);
    }

    for (i = 0; i < l->readonly_paths_len; i++) {
        path = l->readonly_paths[i];
        if (path == NULL) {
            continue;
        }
        node = create_lcr_list_node("lxc.isulad.rootfs.ropaths", path);
        if (node == NULL) {
            goto out_free;
        }
        lcr_list_add_tail(conf, node);
    }

    return conf;

out_free:
    lcr_free_config(conf);
    free(conf);

    return NULL;
}

#define POPULATE_DEVICE_SIZE (300 + PATH_MAX)
/* trans oci linux devices */
static struct lcr_list *trans_oci_linux_devices(const oci_runtime_config_linux *l)
{
    struct lcr_list *conf = NULL;
    struct lcr_list *node = NULL;
    size_t i = 0;
    int nret = 0;
    oci_runtime_defs_linux_device *device = NULL;
    char buf_value[POPULATE_DEVICE_SIZE] = { 0 };

    conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    for (i = 0; i < l->devices_len; i++) {
        device = l->devices[i];

        if (device->type == NULL || device->path == NULL) {
            continue;
        }

        /* lxc.populate_device = PATH_IN_CONTAINER:DEVICETYPE:MAJOR:MINOR:MODE:UID:GID
         * For e.g. lxc.populate_device = /dev/sda:b:8:0:0666:0:0
         */
        nret = sprintf_s(buf_value, sizeof(buf_value), "%s:%s:%lld:%lld:%d:%u:%u", device->path, device->type,
                         (long long int)(device->major), (long long int)(device->minor), device->file_mode, device->uid,
                         device->gid);
        if (nret < 0) {
            ERROR("Failed to get populate device string");
            goto out_free;
        }

        node = create_lcr_list_node("lxc.isulad.populate.device", buf_value);
        if (node == NULL) {
            goto out_free;
        }
        lcr_list_add_tail(conf, node);
    }

    return conf;

out_free:
    lcr_free_config(conf);
    free(conf);

    return NULL;
}

static inline bool is_seccomp_action_kill(const char *value)
{
    return strcmp(value, "SCMP_ACT_KILL") == 0;
}

static inline bool is_seccomp_action_trap(const char *value)
{
    return strcmp(value, "SCMP_ACT_TRAP") == 0;
}

static inline bool is_seccomp_action_allow(const char *value)
{
    return strcmp(value, "SCMP_ACT_ALLOW") == 0;
}

static inline bool is_seccomp_action_trace(const char *value)
{
    return strcmp(value, "SCMP_ACT_TRACE") == 0;
}

static inline bool is_seccomp_action_errno(const char *value)
{
    return strcmp(value, "SCMP_ACT_ERRNO") == 0;
}

/* seccomp trans action */
static char *seccomp_trans_action(const char *action)
{
    if (is_seccomp_action_kill(action)) {
        return util_strdup_s("kill");
    } else if (is_seccomp_action_trap(action)) {
        return util_strdup_s("trap");
    } else if (is_seccomp_action_allow(action)) {
        return util_strdup_s("allow");
    } else if (is_seccomp_action_trace(action)) {
        return util_strdup_s("trace 1");
    } else if (is_seccomp_action_errno(action)) {
        return util_strdup_s("errno 1");
    }

    return NULL;
}

static bool is_action_allow(const char *value)
{
    return strcmp(value, "allow") == 0;
}

#define DEFAULT_ACTION_OFFSET 12
/* seccomp append head info */
static int seccomp_append_head_info(const char *action, Buffer *buffer)
{
    int ret = 0;
    char *default_action = NULL;

    if (action == NULL) {
        return -1;
    }

    default_action = seccomp_trans_action(action);
    if (default_action == NULL) {
        ERROR("Failed to translate seccomp action");
        return -1;
    }

    if (is_action_allow(default_action)) {
        ret = buffer_nappendf(buffer, strlen(default_action) + DEFAULT_ACTION_OFFSET, "blacklist %s\n", default_action);
    } else {
        ret = buffer_nappendf(buffer, strlen(default_action) + DEFAULT_ACTION_OFFSET, "whitelist %s\n", default_action);
    }
    if (ret != 0) {
        ERROR("Failed to append seccomp config head info\n");
        ret = -1;
        goto out;
    }

out:
    free(default_action);
    return ret;
}

/* get hostarch */
static char *get_hostarch(void)
{
    struct utsname uts;
    size_t len;
    size_t i;
    /* no x32 kernels */
    lcr_host_arch arch_type[] = {
        { "i686", "[x86]", 4 },      { "x32", "[x32]", 3 },           { "x86_64", "[x86_64]", 6 },
        { "armv7", "[arm]", 5 },     { "aarch64", "[arm64]", 7 },     { "ppc64le", "[ppc64le]", 7 },
        { "ppc64", "[ppc64]", 5 },   { "ppc", "[ppc]", 3 },           { "mips64n32", "[mips64n32]", 9 },
        { "mips64", "[mips64]", 6 }, { "mips", "[mips]", 4 },         { "s390x", "[s390x]", 5 },
        { "s390", "[s390]", 4 },     { "parisc64", "[parisc64]", 8 }, { "parisc", "[parisc]", 6 },
    };

    if (uname(&uts) < 0) {
        SYSERROR("Failed to read host arch");
        return NULL;
    }
    len = sizeof(arch_type) / sizeof(lcr_host_arch);
    for (i = 0; i < len; i++) {
        if (i == 0 || i == 1 || i == 2) {
            if (strcmp(uts.machine, arch_type[i].arch) == 0) {
                return util_strdup_s(arch_type[i].value);
            }
        } else {
            if (strncmp(uts.machine, arch_type[i].arch, (size_t)(arch_type[i].num)) == 0) {
                return util_strdup_s(arch_type[i].value);
            }
        }
    }

    ERROR("Failed to get machine type");
    return NULL;
}

/* seccomp trans arch */
static char *seccomp_trans_arch(const char *arch)
{
    lcr_arch_value arch_type[] = {
        { "SCMP_ARCH_X86", "[x86]" },
        { "SCMP_ARCH_X86_64", "[x86_64]" },
        { "SCMP_ARCH_X32", "[x32]" },
        { "SCMP_ARCH_ARM", "[arm]" },
        { "SCMP_ARCH_AARCH64", "[arm64]" },
        { "SCMP_ARCH_MIPS", "[mips]" },
        { "SCMP_ARCH_MIPS64", "[mips64]" },
        { "SCMP_ARCH_MIPS64N32", "[mips64n32]" },
        { "SCMP_ARCH_MIPSEL", "[mipsel]" },
        { "SCMP_ARCH_MIPSEL64", "[mipsel64]" },
        { "SCMP_ARCH_MIPSEL64N32", "[mipsel64n32]" },
        { "SCMP_ARCH_PPC", "[ppc]" },
        { "SCMP_ARCH_PPC64", "[ppc64]" },
        { "SCMP_ARCH_PPC64LE", "[ppc64le]" },
        { "SCMP_ARCH_S390", "[s390]" },
        { "SCMP_ARCH_S390X", "[s390x]" },
        { "SCMP_ARCH_PARISC", "[parisc]" },
        { "SCMP_ARCH_PARISC64", "[parisc64]" },
        { "SCMP_ARCH_ALL", "[all]" },
        { "SCMP_ARCH_AUTO", "" },
    };
    size_t len;
    size_t i = 0;
    len = sizeof(arch_type) / sizeof(lcr_arch_value);
    for (i = 0; i < len; i++) {
        if (strcmp(arch_type[i].arch, "SCMP_ARCH_AUTO") == 0) {
            return get_hostarch();
        } else if (strcmp(arch, arch_type[i].arch) == 0) {
            return util_strdup_s(arch_type[i].value);
        }
    }
    return NULL;
}

/* seccomp append arch */
static int seccomp_append_arch(char *arch, Buffer *buffer)
{
    int ret = 0;
    char *trans_arch = NULL;

    if (arch == NULL) {
        return -1;
    }

    trans_arch = seccomp_trans_arch(arch);
    if (trans_arch == NULL) {
        ERROR("Failed to translate seccomp arch: %s", arch);
        return -1;
    }

    if (buffer_nappendf(buffer, strlen(trans_arch) + 2, "%s\n", trans_arch)) {
        ERROR("Failed to append seccomp config head info\n");
        ret = -1;
    }

    free(trans_arch);
    return ret;
}

/* seccomp append rule */
static int seccomp_append_rule(const oci_runtime_defs_linux_syscall *syscall, size_t i, Buffer *buffer, char *action)
{
    int ret = 0;
    size_t j = 0;

    if (syscall->names[i] == NULL) {
        ERROR("Failed to get syscall name");
        ret = -1;
        goto out;
    }
    if (buffer_nappendf(buffer, strlen(syscall->names[i]) + strlen(action) + 2, "%s %s", syscall->names[i], action)) {
        ERROR("Failed to append syscall name and action\n");
        ret = -1;
        goto out;
    }

    for (j = 0; j < syscall->args_len; j++) {
        if ((syscall->args[j] == NULL) || (syscall->args[j]->op == NULL)) {
            ERROR("Failed to get syscall args");
            ret = -1;
            goto out;
        }
        if (buffer_nappendf(buffer, 20 * 3 + strlen(syscall->args[j]->op), " [%u,%llu,%s,%llu]",
                            syscall->args[j]->index, syscall->args[j]->value, syscall->args[j]->op,
                            syscall->args[j]->value_two)) {
            ERROR("Failed to append syscall rules\n");
            ret = -1;
            goto out;
        }
    }

    if (buffer_nappendf(buffer, 2, "\n")) {
        ERROR("Failed to append newline\n");
        ret = -1;
        goto out;
    }
out:
    return ret;
}

/* seccomp append rules */
static int seccomp_append_rules(const oci_runtime_defs_linux_syscall *syscall, Buffer *buffer)
{
    int ret = 0;
    size_t i = 0;
    char *action = NULL;

    if (syscall == NULL) {
        return -1;
    }

    if ((syscall->action == NULL) || syscall->names_len == 0) {
        return -1;
    }

    action = seccomp_trans_action(syscall->action);
    if (action == NULL) {
        ERROR("Failed to translate action");
        ret = -1;
        goto out;
    }

    for (i = 0; i < syscall->names_len; i++) {
        if (seccomp_append_rule(syscall, i, buffer, action)) {
            ret = -1;
            goto out;
        }
    }
out:
    free(action);
    return ret;
}

static struct lcr_list *trans_oci_linux_sysctl(const json_map_string_string *sysctl)
{
    struct lcr_list *conf = NULL;
    struct lcr_list *node = NULL;
    size_t i;

    conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    for (i = 0; i < sysctl->len; i++) {
        char sysk[BUFSIZ] = { 0 };
        int nret = sprintf_s(sysk, sizeof(sysk), "lxc.sysctl.%s", sysctl->keys[i]);
        if (nret < 0) {
            ERROR("Failed to print string");
            goto out_free;
        }
        node = create_lcr_list_node(sysk, sysctl->values[i]);
        if (node == NULL) {
            goto out_free;
        }
        lcr_list_add_tail(conf, node);
    }

    return conf;

out_free:
    lcr_free_config(conf);
    free(conf);
    return NULL;
}

static int append_seccomp_with_archs(const oci_runtime_config_linux_seccomp *seccomp, Buffer *buffer)
{
    int ret = 0;
    size_t i = 0;
    size_t j = 0;

    for (i = 0; i < seccomp->architectures_len; i++) {
        if (seccomp_append_arch(seccomp->architectures[i], buffer)) {
            ret = -1;
            goto out;
        }
        /* append rules*/
        for (j = 0; j < seccomp->syscalls_len; j++) {
            if (seccomp_append_rules(seccomp->syscalls[j], buffer)) {
                ret = -1;
                goto out;
            }
        }
    }
out:
    return ret;
}

/* lxc seccomp conf format:
	To support limit syscall arguments, extend the version 2 file to the following format:

	syscall_name action [index,value,op,valueTwo] [index,value,op]...
	for one arguments, [index,value,valueTwo,op]

	index: the index for syscall arguments (type uint)

	value: the value for syscall arguments (type uint64)

	op: the operator for syscall arguments(string), a valid list of constants as of libseccomp v2.3.2 is
	SCMP_CMP_NE,SCMP_CMP_LE,SCMP_CMP_LE, SCMP_CMP_EQ, SCMP_CMP_GE,
	SCMP_CMP_GT, SCMP_CMP_MASKED_EQ, or !=,<=,==,>=,>,&=

	valueTwo: the value for syscall arguments only used for mask eq (type uint64, optional)

For example:

	2
	blacklist allow
	reject_force_umount  # comment this to allow umount -f;  not recommended
	[all]
	kexec_load errno 1 [0,1,SCMP_CMP_LE][3,1,==][5,1,SCMP_CMP_MASKED_EQ,1]
	open_by_handle_at errno 1
	init_module errno 1
	finit_module errno 1
	delete_module errno 1

*/
static int trans_oci_seccomp(const oci_runtime_config_linux_seccomp *seccomp, char **seccomp_conf)
{
    int ret = 0;
    size_t j = 0;
    size_t init_size = 4 * SIZE_KB;

    Buffer *buffer = buffer_alloc(init_size);
    if (buffer == NULL) {
        ERROR("Failed to malloc output_buffer\n");
        return -1;
    }

    /* config version */
    if (buffer_nappendf(buffer, 3, "2\n")) {
        ERROR("Failed to append seccomp config version\n");
        ret = -1;
        goto out_free;
    }

    /* append head info */
    if (seccomp_append_head_info(seccomp->default_action, buffer)) {
        ret = -1;
        goto out_free;
    }

    /* append architectures */
    if (seccomp->architectures_len != 0) {
        ret = append_seccomp_with_archs(seccomp, buffer);
        if (ret != 0) {
            goto out_free;
        }
    } else {
        // add rules directly(eg: blacklist)
        for (j = 0; j < seccomp->syscalls_len; j++) {
            if (seccomp_append_rules(seccomp->syscalls[j], buffer)) {
                ret = -1;
                goto out_free;
            }
        }
    }
    *seccomp_conf = buffer_to_s(buffer);
    if (*seccomp_conf == NULL) {
        ret = -1;
        goto out_free;
    }

out_free:
    buffer_free(buffer);
    return ret;
}

/* trans oci linux */
struct lcr_list *trans_oci_linux(const oci_runtime_config_linux *l, char **seccomp_conf)
{
    int ret = 0;
    struct lcr_list *tmp = NULL;

    struct lcr_list *conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    // UID/GID Mapping
    tmp = trans_oci_id_mapping(l);
    if (tmp == NULL) {
        goto out_free;
    }
    lcr_list_merge(conf, tmp);

    // Resources
    if (l->resources != NULL) {
        tmp = trans_oci_resources(l->resources);
        if (tmp == NULL) {
            goto out_free;
        }
        lcr_list_merge(conf, tmp);
    }

    // linux devices
    tmp = trans_oci_linux_devices(l);
    if (tmp == NULL) {
        goto out_free;
    }
    lcr_list_merge(conf, tmp);

    // Namespaces
    tmp = trans_oci_namespaces(l);
    if (tmp == NULL) {
        goto out_free;
    }
    lcr_list_merge(conf, tmp);

    // MaskedPaths and ReadonlyPaths
    tmp = trans_oci_mask_ro_paths(l);
    if (tmp == NULL) {
        goto out_free;
    }
    lcr_list_merge(conf, tmp);

    // sysctl
    if (l->sysctl != NULL && l->uid_mappings == NULL && l->gid_mappings == NULL) {
        tmp = trans_oci_linux_sysctl(l->sysctl);
        if (tmp == NULL) {
            goto out_free;
        }
        lcr_list_merge(conf, tmp);
    }

    // seccomp
    if (l->seccomp != NULL && seccomp_conf != NULL) {
        ret = trans_oci_seccomp(l->seccomp, seccomp_conf);
        if (ret) {
            goto out_free;
        }
    }

    return conf;
out_free:
    lcr_free_config(conf);
    free(conf);

    return NULL;
}

/* trans annotations */
struct lcr_list *trans_annotations(const json_map_string_string *anno)
{
    size_t i, j;
    size_t len;
    int ret = 0;

    struct lcr_list *conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    len = sizeof(g_require_annotations) / sizeof(lcr_annotation_item_t);

    // trans annotations
    for (i = 0; i < anno->len; i++) {
        if (anno->keys[i] == NULL) {
            continue;
        }
        for (j = 0; j < len; j++) {
            if (strcmp(anno->keys[i], g_require_annotations[j].name) != 0) {
                continue;
            }

            ret = g_require_annotations[j].checker(anno->values[i]);
            if (ret == -1) {
                ERROR("item: %s, value: %s, checker failed", anno->keys[i], anno->values[i]);
                goto out_free;
            } else if (ret == 1) {
                DEBUG("Skip this config item: %s", anno->keys[i]);
                continue;
            }

            struct lcr_list *node = create_lcr_list_node(g_require_annotations[j].lxc_item_name, anno->values[i]);
            if (node == NULL) {
                goto out_free;
            }
            lcr_list_add_tail(conf, node);
            break;
        }
    }

    return conf;
out_free:
    lcr_free_config(conf);
    free(conf);

    return NULL;
}

static int add_needed_pty_conf(struct lcr_list *conf)
{
    struct lcr_list *node = create_lcr_list_node("lxc.pty.max", "1024");
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);

    return 0;
}

static int add_needed_tty_conf(struct lcr_list *conf)
{
    struct lcr_list *node = create_lcr_list_node("lxc.tty.max", "4");
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);

    node = create_lcr_list_node("lxc.tty.dir", "lxc");
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);
    return 0;
}

static int add_needed_net_conf(struct lcr_list *conf)
{
    struct lcr_list *node = create_lcr_list_node("lxc.net.0.type", "empty");
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);

    node = create_lcr_list_node("lxc.net.0.flags", "up");
    if (node == NULL) {
        return -1;
    }
    lcr_list_add_tail(conf, node);
    return 0;
}

/* get needed lxc conf */
struct lcr_list *get_needed_lxc_conf()
{
    struct lcr_list *conf = util_common_calloc_s(sizeof(*conf));
    if (conf == NULL) {
        return NULL;
    }
    lcr_list_init(conf);

    if (add_needed_tty_conf(conf) < 0) {
        goto out_free;
    }
    if (add_needed_pty_conf(conf) < 0) {
        goto out_free;
    }
    if (add_needed_net_conf(conf) < 0) {
        goto out_free;
    }

    return conf;
out_free:
    lcr_free_config(conf);
    free(conf);
    return NULL;
}
