From d0442316761717849deb248b5da45240f7bb1d38 Mon Sep 17 00:00:00 2001
From: liuxu <liuxu156@huawei.com>
Date: Thu, 11 Apr 2024 11:01:44 +0800
Subject: [PATCH 67/69] cdi:support modules container_edits/parser

---
 src/daemon/modules/api/specs_api.h            |  13 +
 .../device/cdi/behavior/cdi_container_edits.c | 622 +++++++++++++++++-
 .../device/cdi/behavior/cdi_container_edits.h |   2 +-
 .../device/cdi/behavior/parser/cdi_parser.c   | 196 +++++-
 .../device/cdi/behavior/parser/cdi_parser.h   |   6 +-
 src/daemon/modules/spec/specs.c               | 230 +++++++
 6 files changed, 1052 insertions(+), 17 deletions(-)

diff --git a/src/daemon/modules/api/specs_api.h b/src/daemon/modules/api/specs_api.h
index f54c0d31..0999836b 100644
--- a/src/daemon/modules/api/specs_api.h
+++ b/src/daemon/modules/api/specs_api.h
@@ -55,6 +55,19 @@ const oci_runtime_spec *get_readonly_default_oci_spec(bool system_container);
 
 int spec_module_init(void);
 
+#ifdef ENABLE_CDI
+int defs_process_add_multiple_env(defs_process *dp, const char **envs, size_t env_len);
+int spec_add_multiple_process_env(oci_runtime_spec *oci_spec, const char **envs, size_t env_len);
+int spec_add_device(oci_runtime_spec *oci_spec, defs_device *device);
+int spec_add_linux_resources_device(oci_runtime_spec *oci_spec, bool allow, const char *dev_type,
+                                    int64_t major, int64_t minor, const char *access);
+void spec_remove_mount(oci_runtime_spec *oci_spec, const char *dest);
+int spec_add_mount(oci_runtime_spec *oci_spec, defs_mount *mnt);
+int spec_add_prestart_hook(oci_runtime_spec *oci_spec, defs_hook *prestart_hook);
+int spec_add_poststart_hook(oci_runtime_spec *oci_spec, defs_hook *poststart_hook);
+int spec_add_poststop_hook(oci_runtime_spec *oci_spec, defs_hook *poststop_hook);
+#endif /* ENABLE_CDI */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/daemon/modules/device/cdi/behavior/cdi_container_edits.c b/src/daemon/modules/device/cdi/behavior/cdi_container_edits.c
index 590118b1..816b9c2d 100644
--- a/src/daemon/modules/device/cdi/behavior/cdi_container_edits.c
+++ b/src/daemon/modules/device/cdi/behavior/cdi_container_edits.c
@@ -14,36 +14,642 @@
  ******************************************************************************/
 #include "cdi_container_edits.h"
 
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <isula_libutils/auto_cleanup.h>
+#include <isula_libutils/log.h>
+#include <isula_libutils/utils_array.h>
+
+#include "error.h"
+#include "path.h"
+#include "specs_extend.h"
+#include "utils.h"
+#include "utils_array.h"
+#include "specs_api.h"
+
+/* 
+ * The OCI being used by the iSulad not supportes 
+ * createRuntime/createContainer/startContainer currently.
+ */
 // PRESTART_HOOK is the name of the OCI "prestart" hook.
 #define PRESTART_HOOK "prestart"
-// CREATE_RUNTIME_HOOK is the name of the OCI "createRuntime" hook.
-#define CREATE_RUNTIME_HOOK "createRuntime"
-// CREATE_CONTAINER_HOOK is the name of the OCI "createContainer" hook.
-#define CREATE_CONTAINER_HOOK "createContainer"
-// START_CONTAINER_HOOK is the name of the OCI "startContainer" hook.
-#define START_CONTAINER_HOOK "startContainer"
 // POSTSTART_HOOK is the name of the OCI "poststart" hook.
 #define POSTSTART_HOOK "poststart"
 // POSTSTOP_HOOK is the name of the OCI "poststop" hook.
 #define POSTSTOP_HOOK "poststop"
 
+#define VALID_HOOK_NAME_LEN     3
+static const char* g_valid_hook_names[VALID_HOOK_NAME_LEN] = {
+    PRESTART_HOOK, POSTSTART_HOOK, POSTSTOP_HOOK
+};
+
+static int cdi_validate_env(char **envs, size_t envs_len);
+static int cdi_validate_device_node(cdi_device_node *d);
+static int cdi_validate_hook(cdi_hook *h);
+static int cdi_validate_mount(cdi_mount *m);
+
+#define BLOCK_DEVICE    "b"
+#define CHAR_DEVICE     "c"
+#define FIFO_DEVICE     "p"
+static int device_info_from_path(const char *path, char **dev_type, int64_t *major, int64_t *minor)
+{
+    struct stat stat = { 0 };
+    int ret = 0;
+
+    ret = lstat(path, &stat);
+    if (ret != 0) {
+        ERROR("Failed to stat %s", path);
+        return -1;
+    }
+
+    if (S_ISBLK(stat.st_mode)) {
+        *dev_type = util_strdup_s(BLOCK_DEVICE);
+    } else if (S_ISCHR(stat.st_mode)) {
+        *dev_type = util_strdup_s(CHAR_DEVICE);
+    } else if (S_ISFIFO(stat.st_mode)) {
+        *dev_type = util_strdup_s(FIFO_DEVICE);
+    } else {
+        *dev_type = NULL;
+        *major = 0;
+        *minor = 0;
+        ERROR("Not a device node");
+        return -1;
+    }
+
+    *major = (int64_t)major(stat.st_rdev);
+    *minor = (int64_t)minor(stat.st_rdev);
+    return 0;
+}
+
+static int fill_device_node_info(cdi_device_node *d)
+{
+    __isula_auto_free char *dev_type = NULL;
+    int64_t major;
+    int64_t minor;
+
+    if (d->host_path == NULL) {
+        d->host_path = util_strdup_s(d->path);
+    }
+
+    if (d->type != NULL && (d->major != 0 || strcmp(d->type, FIFO_DEVICE) == 0)) {
+        return 0;
+    }
+
+    if (device_info_from_path(d->host_path, &dev_type, &major, &minor) != 0) {
+        ERROR("Failed to stat CDI host device %s", d->host_path);
+        return -1;
+    }
+
+    if (d->type == NULL) {
+        d->type = dev_type;
+        dev_type = NULL;
+    } else {
+        if (strcmp(d->type, dev_type) != 0) {
+            ERROR("CDI device (%s, %s), host type mismatch (%s, %s)", 
+                d->path, d->host_path, d->type, dev_type);
+            return -1;
+        }
+    }
+    if (d->major == 0 && strcmp(d->type, FIFO_DEVICE) != 0) {
+        d->major = major;
+        d->minor = minor;
+    }
+    return 0;
+}
+
+static cdi_device_node *clone_cdi_device_node(cdi_device_node *d)
+{
+    cdi_device_node *device_node = NULL;
+
+    device_node = util_common_calloc_s(sizeof(*device_node));
+    if (device_node == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+
+    device_node->path = util_strdup_s(d->path);
+    device_node->host_path = util_strdup_s(d->host_path);
+    device_node->type = util_strdup_s(d->type);
+    device_node->major = d->major;
+    device_node->minor = d->minor;
+    device_node->file_mode = d->file_mode;
+    device_node->permissions = util_strdup_s(d->permissions);
+    device_node->uid = d->uid;
+    device_node->gid = d->gid;
+    return device_node;
+}
+
+static cdi_hook *clone_cdi_hook(cdi_hook *h)
+{
+    cdi_hook *hook = NULL;
+
+    hook = util_common_calloc_s(sizeof(*hook));
+    if (hook == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+
+    hook->hook_name = util_strdup_s(h->hook_name);
+    hook->path = util_strdup_s(h->path);
+    if (h->args_len != 0) {
+        hook->args = util_copy_array_by_len(h->args, h->args_len);
+        if (hook->args == NULL) {
+            ERROR("Failed to copy args");
+            goto error_out;
+        }
+        hook->args_len = h->args_len;
+    }
+    if (h->env_len != 0) {
+        hook->env = util_copy_array_by_len(h->env, h->env_len);
+        if (hook->env == NULL) {
+            ERROR("Failed to copy env");
+            goto error_out;
+        }
+        hook->env_len = h->env_len;
+    }
+    hook->timeout = h->timeout;
+   
+    return hook;
+
+error_out:
+    free_cdi_hook(hook);
+    return NULL;
+}
+
+static cdi_mount *clone_cdi_mount(cdi_mount *m)
+{
+    cdi_mount *mount = NULL;
+
+    mount = util_common_calloc_s(sizeof(*mount));
+    if (mount == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+
+    mount->host_path = util_strdup_s(m->host_path);
+    mount->container_path = util_strdup_s(m->container_path);
+    if (m->options_len != 0) {
+        mount->options = util_copy_array_by_len(m->options, m->options_len);
+        if (mount->options == NULL) {
+            ERROR("Failed to copy options");
+            free_cdi_mount(mount);
+            return NULL;
+        }
+        mount->options_len = m->options_len;
+    }
+    mount->type = util_strdup_s(m->type);
+
+    return mount;
+}
+
+static defs_hook *cdi_hook_to_oci(cdi_hook *h)
+{
+    defs_hook *oci_hook = NULL;
+
+    oci_hook = util_common_calloc_s(sizeof(*oci_hook));
+    if (oci_hook == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+
+    oci_hook->path = util_strdup_s(h->path);
+    if (h->args_len != 0) {
+        oci_hook->args = util_copy_array_by_len(h->args, h->args_len);
+        if (oci_hook->args == NULL) {
+            ERROR("Failed to copy args");
+            goto error_out;
+        }
+        oci_hook->args_len = h->args_len;
+    }
+    if (h->env_len != 0) {
+        oci_hook->env = util_copy_array_by_len(h->env, h->env_len);
+        if (oci_hook->env == NULL) {
+            ERROR("Failed to copy env");
+            goto error_out;
+        }
+        oci_hook->env_len = h->env_len;
+    }
+    oci_hook->timeout = h->timeout;
+    return oci_hook;
+
+error_out:
+    free_defs_hook(oci_hook);
+    return NULL;
+}
+
+static defs_mount *cdi_mount_to_oci(cdi_mount *m)
+{
+    defs_mount *oci_mount = NULL;
+
+    oci_mount = util_common_calloc_s(sizeof(*oci_mount));
+    if (oci_mount == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+
+    oci_mount->source = util_strdup_s(m->host_path);
+    oci_mount->destination = util_strdup_s(m->container_path);
+    if (m->options_len != 0) {
+        oci_mount->options = util_copy_array_by_len(m->options, m->options_len);
+        if (oci_mount->options == NULL) {
+            ERROR("Failed to copy options");
+            free_defs_mount(oci_mount);
+            return NULL;
+        }
+        oci_mount->options_len = m->options_len;
+    }
+    oci_mount->type = util_strdup_s(m->type);
+
+    return oci_mount;
+}
+
+static defs_device *cdi_device_node_to_oci(cdi_device_node *d)
+{
+    defs_device *oci_device = NULL;
+
+    oci_device = util_common_calloc_s(sizeof(*oci_device));
+    if (oci_device == NULL) {
+        ERROR("Out of memory");
+        return NULL;
+    }
+
+    oci_device->path = util_strdup_s(d->path);
+    oci_device->type = util_strdup_s(d->type);
+    oci_device->major = d->major;
+    oci_device->minor = d->minor;
+    oci_device->file_mode = d->file_mode;
+    oci_device->uid = d->uid;
+    oci_device->gid = d->gid;
+    
+    return oci_device;
+}
+
+static int apply_cdi_device_nodes(cdi_container_edits *e, oci_runtime_spec *spec)
+{
+    size_t i;
+    defs_device *dev = NULL;
+    cdi_device_node *dn = NULL;
+    const char *access = NULL;
+
+    for (i = 0; i < e->device_nodes_len; i++) {
+        dn = clone_cdi_device_node(e->device_nodes[i]);
+        if (dn == NULL) {
+            ERROR("Failed to copy device node");
+            goto error_out;
+        }
+
+        if (fill_device_node_info(dn) != 0) {
+            goto error_out;
+        }
+        dev = cdi_device_node_to_oci(dn);
+        if (dev == NULL) {
+            ERROR("Failed to generate oci device");
+            goto error_out;
+        }
+        /* Currently, for uid and gid, isulad cannot distinguish
+         * 0 and unspecified. Here, 0 is processed as unspecified.
+         */
+        if (dev->uid == 0 && spec->process != NULL) {
+            if (spec->process->user->uid > 0) {
+                dev->uid = spec->process->user->uid;
+            }
+        }
+        if (dev->gid == 0 && spec->process != NULL) {
+            if (spec->process->user->gid > 0) {
+                dev->gid = spec->process->user->gid;
+            }
+        }
+
+        if (spec_add_device(spec, dev) != 0) {
+            goto error_out;
+        }
+
+        if (strcmp(dev->type, BLOCK_DEVICE) == 0 || strcmp(dev->type, CHAR_DEVICE) == 0) {
+            if (e->device_nodes[i]->permissions != NULL) {
+                access = e->device_nodes[i]->permissions;
+            } else {
+                access = "rwm";
+            }
+            if (spec_add_linux_resources_device(spec, true, dev->type,
+                dev->major, dev->minor, access)) {
+                dev = NULL;
+                goto error_out;
+            }
+        }
+        free_cdi_device_node(dn);
+        dn = NULL;
+        dev = NULL;
+    }
+
+    return 0;
+
+error_out:
+    free_cdi_device_node(dn);
+    free_defs_device(dev);
+    return -1;
+}
+
+static int defs_mount_parts(defs_mount *m)
+{
+    char cleanpath[PATH_MAX] = { 0 };
+    if (util_clean_path(m->destination, cleanpath, sizeof(cleanpath)) == NULL) {
+        return -1;
+    }
+
+    return util_strings_count(cleanpath, '/');
+}
+
+static inline int defs_mount_cmp(defs_mount **first, defs_mount **second)
+{
+    int first_part = defs_mount_parts(*first);
+    int second_part = defs_mount_parts(*second);
+
+    if (first_part < second_part) {
+        return -1;
+    }
+    if (first_part > second_part) {
+        return 1;
+    }
+
+    return strcmp((*first)->destination, (*second)->destination);
+}
+
+static int apply_cdi_mounts(cdi_container_edits *e, oci_runtime_spec *spec)
+{
+    size_t i;
+    defs_mount *mnt = NULL;
+
+    if (e->mounts_len == 0) {
+        return 0;
+    }
+
+    for (i = 0; i < e->mounts_len; i++) {
+        spec_remove_mount(spec, e->mounts[i]->container_path);
+        mnt = cdi_mount_to_oci(e->mounts[i]);
+        if (spec_add_mount(spec, mnt) != 0) {
+            free_defs_mount(mnt);
+            return -1;
+        }
+    }
+        
+    qsort(spec->mounts, spec->mounts_len,
+              sizeof(defs_mount *), (int (*)(const void *, const void *))defs_mount_cmp);
+    return 0;
+}
+
+static int apply_cdi_hooks(cdi_container_edits *e, oci_runtime_spec *spec)
+{
+    size_t i;
+    int ret = 0;
+
+    for (i = 0; i < e->hooks_len; i++) {
+        defs_hook *oci_hook = cdi_hook_to_oci(e->hooks[i]);
+        if (strcmp(e->hooks[i]->hook_name, PRESTART_HOOK)) {
+            ret = spec_add_prestart_hook(spec, oci_hook);
+        } else if (strcmp(e->hooks[i]->hook_name, POSTSTART_HOOK)) {
+            ret = spec_add_poststart_hook(spec, oci_hook);
+        } else if (strcmp(e->hooks[i]->hook_name, POSTSTOP_HOOK)) {
+            ret = spec_add_poststop_hook(spec, oci_hook);
+        } else {
+            /* 
+            * The OCI being used by the iSulad not supportes 
+            * createRuntime/createContainer/startContainer currently.
+            */
+            ERROR("Unknown hook name %s", e->hooks[i]->hook_name);
+            free_defs_hook(oci_hook);
+            return -1;
+        }
+        if (ret != 0) {
+            ERROR("Failed add hook %s", e->hooks[i]->hook_name);
+            free_defs_hook(oci_hook);
+            return -1;
+        }
+    }
+    return ret;
+}
+
 int cdi_container_edits_apply(cdi_container_edits *e, oci_runtime_spec *spec)
 {
+    if (spec == NULL) {
+        ERROR("Can't edit nil OCI Spec");
+        return -1;
+    }
+    if (e == NULL) {
+        WARN("Cdi container edits is nil");
+        return 0;
+    }
+
+    if (e->env_len > 0) {
+        if (spec_add_multiple_process_env(spec, (const char **)e->env, e->env_len) != 0) {
+            ERROR("Failed to merge envs");
+            return -1;
+        }
+    }
+
+    if (apply_cdi_device_nodes(e, spec) != 0) {
+        ERROR("Failed to apply device nodes");
+        return -1;
+    }
+
+    if (apply_cdi_mounts(e, spec) != 0) {
+        ERROR("Failed to apply mounts");
+        return -1;
+    }
+
+    if (apply_cdi_hooks(e, spec) != 0) {
+        ERROR("Failed to apply hooks");
+        return -1;
+    }
+
     return 0;
 }
 
-int cdi_container_edits_validate(cdi_container_edits *e, char **error)
+int cdi_container_edits_validate(cdi_container_edits *e)
 {
+    size_t i;
+
+    if (e == NULL) {
+        WARN("Cdi container edits is nil");
+        return 0;
+    }
+
+    if (cdi_validate_env(e->env, e->env_len) != 0) {
+        ERROR("Invalid container edits");
+        return -1;
+    }
+    for (i = 0; i < e->device_nodes_len; i++) {
+        if (cdi_validate_device_node(e->device_nodes[i]) != 0) {
+            ERROR("Invalid container device node");
+            return -1;
+        }
+    }
+    for (i = 0; i < e->hooks_len; i++) {
+        if (cdi_validate_hook(e->hooks[i]) != 0) {
+            ERROR("Invalid container hook");
+            return -1;
+        }
+    }
+    for (i = 0; i < e->mounts_len; i++) {
+        if (cdi_validate_mount(e->mounts[i]) != 0) {
+            ERROR("Invalid container mount");
+            return -1;
+        }
+    }
+
     return 0;
 }
 
+#define EDITS_APPEND_ITEM_DEF(item)                                                                     \
+    static int append_##item(cdi_container_edits *e, cdi_container_edits *o, clone_common_array_item_cb cb) \
+    {                                                                                                   \
+        common_array e_array = {                                                                        \
+            .items = (void **)e->item,                                                                  \
+            .len = e->item##_len,                                                                       \
+            .cap = e->item##_len,                                                                       \
+            .free_item_cb = NULL,                                                                       \
+            .clone_item_cb = cb                                                                         \
+        };                                                                                              \
+        common_array o_array = {                                                                        \
+            .items = (void **)o->item,                                                                  \
+            .len = o->item##_len,                                                                       \
+            .cap = o->item##_len,                                                                       \
+            .free_item_cb = NULL,                                                                       \
+            .clone_item_cb = cb                                                                         \
+        };                                                                                              \
+        if (util_merge_common_array(&e_array, &o_array) != 0) {                                         \
+            ERROR("Out of memory");                                                                     \
+            return -1;                                                                                  \
+        }                                                                                               \
+        e->item = (void *)e_array.items;                                                                \
+        e->item##_len += o->item##_len;                                                                 \
+        return 0;                                                                                       \
+    }
+
+EDITS_APPEND_ITEM_DEF(env)
+EDITS_APPEND_ITEM_DEF(device_nodes)
+EDITS_APPEND_ITEM_DEF(hooks)
+EDITS_APPEND_ITEM_DEF(mounts)
+
 int cdi_container_edits_append(cdi_container_edits *e, cdi_container_edits *o)
 {
+    if (o == NULL) {
+        return 0;
+    }
+    if (e == NULL) {
+        ERROR("Invalid params");
+        return -1;
+    }
+
+    if (append_env(e, o, (clone_common_array_item_cb)util_strdup_s) != 0) {
+        return -1;
+    }
+    if (append_device_nodes(e, o, (clone_common_array_item_cb)clone_cdi_device_node) != 0) {
+        return -1;
+    }
+    if (append_hooks(e, o, (clone_common_array_item_cb)clone_cdi_hook) != 0) {
+        return -1;
+    }
+    if (append_mounts(e, o, (clone_common_array_item_cb)clone_cdi_mount) != 0) {
+        return -1;
+    }
+
     return 0;
 }
 
 bool cdi_container_edits_is_empty(cdi_container_edits *e)
 {
-    return true;
+    if (e == NULL) {
+        return false;
+    }
+    return e->env_len + e->device_nodes_len + e->hooks_len + e->mounts_len == 0;
 }
 
+static int cdi_validate_env(char **envs, size_t envs_len)
+{
+    size_t i;
+    char *ptr = NULL;
+
+    for (i = 0; i < envs_len; i++) {
+        ptr = strchr(envs[i], '=');
+        if (ptr == NULL || ptr == envs[i]) {
+            ERROR("Invalid environment variable %s", envs[i]);
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int cdi_validate_device_node(cdi_device_node *d)
+{
+    char *p = NULL;
+
+    if (d == NULL) {
+        ERROR("Device node is nil");
+        return -1;
+    }
+
+    if (d->path == NULL) {
+        ERROR("Invalid (empty) device path");
+        return -1;
+    }
+    if (d->type != NULL && strcmp(d->type, BLOCK_DEVICE) != 0 &&
+        strcmp(d->type, CHAR_DEVICE) != 0 && strcmp(d->type, FIFO_DEVICE) != 0) {
+        ERROR("Device %s: invalid type %s", d->path, d->type);
+        return -1;
+    }
+    for (p = d->permissions; p != NULL && *p != '\0'; p++) {
+        if (*p != 'r' && *p != 'w' && *p != 'm') {
+            ERROR("Device %s: invalid permissions %s", d->path, d->permissions);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int cdi_validate_hook(cdi_hook *h)
+{
+    size_t i;
+
+    if (h == NULL) {
+        ERROR("Hook is nil");
+        return -1;
+    }
+
+    for (i = 0; i < VALID_HOOK_NAME_LEN; i++) {
+        if (strcmp(h->hook_name, g_valid_hook_names[i]) == 0) {
+            break;
+        }
+    }
+    if (i == VALID_HOOK_NAME_LEN) {
+        ERROR("Invalid hook name %s", h->hook_name);
+        return -1;
+    }
+    if (h->path == NULL) {
+        ERROR("Invalid hook %s with empty path", h->hook_name);
+        return -1;
+    }
+    if (cdi_validate_env(h->env, h->env_len) != 0) {
+        ERROR("Invalid hook %s", h->hook_name);
+        return -1;
+    }
+    return 0;
+}
+
+static int cdi_validate_mount(cdi_mount *m)
+{
+    if (m == NULL) {
+        ERROR("Mount is nil");
+        return -1;
+    }
+
+    if (m->host_path == NULL) {
+        ERROR("Invalid mount, empty host path");
+        return -1;
+    }
+    if (m->container_path == NULL) {
+        ERROR("Invalid mount, empty container path");
+        return -1;
+    }
+    return 0;
+}
diff --git a/src/daemon/modules/device/cdi/behavior/cdi_container_edits.h b/src/daemon/modules/device/cdi/behavior/cdi_container_edits.h
index ea921e37..ddceec25 100644
--- a/src/daemon/modules/device/cdi/behavior/cdi_container_edits.h
+++ b/src/daemon/modules/device/cdi/behavior/cdi_container_edits.h
@@ -28,7 +28,7 @@ extern "C" {
 #endif
 
 int cdi_container_edits_apply(cdi_container_edits *e, oci_runtime_spec *spec);
-int cdi_container_edits_validate(cdi_container_edits *e, char **error);
+int cdi_container_edits_validate(cdi_container_edits *e);
 int cdi_container_edits_append(cdi_container_edits *e, cdi_container_edits *o);
 bool cdi_container_edits_is_empty(cdi_container_edits *e);
 
diff --git a/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.c b/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.c
index 14293c72..8824d29c 100644
--- a/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.c
+++ b/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.c
@@ -14,42 +14,228 @@
  ******************************************************************************/
 #include "cdi_parser.h"
 
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <isula_libutils/log.h>
+#include <isula_libutils/auto_cleanup.h>
+
+#include "error.h"
+#include "utils_string.h"
+
+/* cdi_parser_qualified_name returns the qualified name for a device.
+ * The syntax for a qualified device names is
+ *
+ *	"<vendor>/<class>=<name>".
+ *
+ * A valid vendor and class name may contain the following runes:
+ *
+ *	'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'.
+ *
+ * A valid device name may contain the following runes:
+ *
+ *	'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':'
+ */
 char *cdi_parser_qualified_name(const char *vendor, const char *class, const char *name)
 {
-    return NULL;
+    char device_name[PATH_MAX] = { 0 };
+    int nret;
+
+    if (vendor == NULL || class == NULL || name == NULL) {
+        ERROR("Invalid argument");
+        return NULL;
+    }
+
+    nret = snprintf(device_name, sizeof(device_name), "%s/%s=%s",
+                        vendor, class, name);
+    if (nret < 0 || (size_t)nret >= sizeof(device_name)) {
+        ERROR("Device name is too long");
+        return NULL;
+    }
+    return util_strdup_s(device_name);
 }
 
+// cdi_parser_is_qualified_name tests if a device name is qualified.
 bool cdi_parser_is_qualified_name(const char *device)
 {
-    return true;
+    __isula_auto_free char *vendor = NULL;
+    __isula_auto_free char *class = NULL;
+    __isula_auto_free char *name = NULL;
+
+    return cdi_parser_parse_qualified_name(device, &vendor, &class, &name) == 0;
 }
 
+// cdi_parser_parse_qualified_name splits a qualified name into device vendor, class, and name.
 int cdi_parser_parse_qualified_name(const char *device, char **vendor, char **class, char **name)
 {
+    int ret = 0;
+
+    ret = cdi_parser_parse_device(device, vendor, class, name);
+    if (ret != 0) {
+        if (*vendor == NULL) {
+            ERROR("Unqualified device %s, missing vendor", device);
+            return -1;
+        }
+        if (*class == NULL) {
+            ERROR("Unqualified device %s, missing class", device);
+            return -1;
+        }
+        if (*name == NULL) {
+            ERROR("Unqualified device %s, missing name", device);
+            return -1;
+        }
+        ERROR("Unqualified device %s", device);
+        return -1;
+    }
+
+    if (cdi_parser_validate_vendor_name(*vendor) != 0) {
+        ERROR("Invalid device %s", device);
+        goto err_out;
+    }
+    if (cdi_parser_validate_class_name(*class) != 0) {
+        ERROR("Invalid device %s", device);
+        goto err_out;
+    }
+    if (cdi_parser_validate_device_name(*name) != 0) {
+        ERROR("Invalid device %s", device);
+        goto err_out;
+    }
+
     return 0;
+
+err_out:
+    free(*vendor);
+    *vendor = NULL;
+    free(*class);
+    *class = NULL;
+    free(*name);
+    *name = NULL;
+    return -1;
 }
 
+// cdi_parser_parse_device tries to split a device name into vendor, class, and name.
 int cdi_parser_parse_device(const char *device, char **vendor, char **class, char **name)
 {
+    __isula_auto_array_t char **parts = NULL;
+
+    if (vendor == NULL || class == NULL || name == NULL || 
+        device == NULL || device[0] == '/') {
+        ERROR("Invalid argument");
+        return -1;
+    }
+
+    parts = util_string_split_n(device, '=', 2);
+    if (parts == NULL || util_array_len((const char **)parts) != 2 || parts[0] == NULL || parts[1] == NULL) {
+        return -1;
+    }
+
+    *name = parts[1];
+    parts[1] = NULL;
+    (void)cdi_parser_parse_qualifier(parts[0], vendor, class);
+    if (*vendor == NULL) {
+        ERROR("Failed to parse device qualifier: %s", parts[0]);
+        return -1;
+    }
+
     return 0;
 }
 
+/* cdi_parser_parse_qualifier splits a device qualifier into vendor and class.
+ * The syntax for a device qualifier is
+ *
+ *	"<vendor>/<class>"
+ */
 int cdi_parser_parse_qualifier(const char *kind, char **vendor, char **class)
 {
+    __isula_auto_array_t char **parts = NULL;
+
+    if (kind == NULL || vendor == NULL || class == NULL) {
+        ERROR("Invalid argument");
+        return -1;
+    }
+
+    parts = util_string_split_n(kind, '/', 2);
+    if (parts == NULL || util_array_len((const char **)parts) != 2 || parts[0] == NULL || parts[1] == NULL) {
+        return -1;
+    }
+    *vendor = parts[0];
+    parts[0] = NULL;
+    *class = parts[1];
+    parts[1] = NULL;
+
+    return 0;
+}
+
+static int validate_vendor_or_class_name(const char *name)
+{
+    int i = 0;
+
+    if (name == NULL) {
+        ERROR("Empty name");
+        return -1;
+    }
+
+    if (!isalpha(name[0])) {
+        ERROR("%s, should start with letter", name);
+        return -1;
+    }
+    for (i = 1; name[i] != '\0'; i++) {
+        if (!(isalnum(name[i]) || name[i] == '_' || name[i] == '-' || name[i] == '.')) {
+            ERROR("Invalid character '%c' in name %s", name[i], name);
+            return -1;
+        }
+    }
+    if (!isalnum(name[i - 1])) {
+        ERROR("%s, should end with a letter or digit", name);
+        return -1;
+    }
+
     return 0;
 }
 
-int cdi_parser_validate_vendor_name(const char *vendor, char **error)
+int cdi_parser_validate_vendor_name(const char *vendor)
 {
+    if (validate_vendor_or_class_name(vendor) != 0) {
+        ERROR("Invalid vendor");
+        return -1;
+    }
     return 0;
 }
 
-int cdi_parser_validate_class_name(const char *class, char **error)
+int cdi_parser_validate_class_name(const char *class)
 {
+    if (validate_vendor_or_class_name(class) != 0) {
+        ERROR("Invalid class.");
+        return -1;
+    }
     return 0;
 }
 
-int cdi_parser_validate_device_name(const char *name, char **error)
+int cdi_parser_validate_device_name(const char *name)
 {
+    size_t i;
+    
+    if (name == NULL) {
+        ERROR("Invalid (empty) device name");
+        return -1;
+    }
+    if (!isalnum(name[0])) {
+        ERROR("Invalid class %s, should start with a letter or digit", name);
+        return -1;
+    }
+    if (strlen(name) == 1) {
+        return 0;
+    }
+    for (i = 1; name[i] != '\0'; i++) {
+        if (!(isalnum(name[i]) || name[i] == '_' || name[i] == '-' || name[i] == '.' || name[i] == ':')) {
+            ERROR("Invalid character '%c' in device name %s", name[i], name);
+            return -1;
+        }
+    }
+    if (!isalnum(name[i - 1])) {
+        ERROR("Invalid name %s, should end with a letter or digit", name);
+        return -1;
+    }
+
     return 0;
 }
diff --git a/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.h b/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.h
index 467641a1..3658e29b 100644
--- a/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.h
+++ b/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.h
@@ -27,9 +27,9 @@ bool cdi_parser_is_qualified_name(const char *device);
 int cdi_parser_parse_qualified_name(const char *device, char **vendor, char **class, char **name);
 int cdi_parser_parse_device(const char *device, char **vendor, char **class, char **name);
 int cdi_parser_parse_qualifier(const char *kind, char **vendor, char **class);
-int cdi_parser_validate_vendor_name(const char *vendor, char **error);
-int cdi_parser_validate_class_name(const char *class, char **error);
-int cdi_parser_validate_device_name(const char *name, char **error);
+int cdi_parser_validate_vendor_name(const char *vendor);
+int cdi_parser_validate_class_name(const char *class);
+int cdi_parser_validate_device_name(const char *name);
 
 #ifdef __cplusplus
 }
diff --git a/src/daemon/modules/spec/specs.c b/src/daemon/modules/spec/specs.c
index b4d2b0f6..77ca70f9 100644
--- a/src/daemon/modules/spec/specs.c
+++ b/src/daemon/modules/spec/specs.c
@@ -29,6 +29,7 @@
 #include <isula_libutils/host_config.h>
 #include <isula_libutils/log.h>
 #include <isula_libutils/auto_cleanup.h>
+#include <isula_libutils/utils_array.h>
 
 #include "specs_api.h"
 #include "utils.h"
@@ -2595,3 +2596,232 @@ int spec_module_init(void)
     return 0;
 }
 
+#ifdef ENABLE_CDI
+static int add_env(defs_process *dp, const char *env, const char *key)
+{
+    size_t i;
+    char *oci_key = NULL;
+    char *oci_value = NULL;
+    char *saveptr = NULL;
+    __isula_auto_free char *tmp_env = NULL;
+ 
+    for (i = 0; i < dp->env_len; i++) {
+        tmp_env = util_strdup_s(dp->env[i]);
+        oci_key = strtok_r(tmp_env, "=", &saveptr);
+        oci_value = strtok_r(NULL, "=", &saveptr);
+        if (oci_key == NULL || oci_value == NULL) {
+            ERROR("Bad env format");
+            return -1;
+        }
+        if (strcmp(key, oci_key) == 0) {
+            free(dp->env[i]);
+            dp->env[i] = util_strdup_s(env);
+            return 0;
+        }
+        free(tmp_env);
+        tmp_env = NULL;
+    }
+    if (util_mem_realloc((void **)&dp->env, (dp->env_len + 1) * sizeof(char *),
+                         (void *)dp->env, dp->env_len * sizeof(char *)) != 0) {
+        ERROR("Out of memory");
+        return -1;
+    }
+    dp->env[dp->env_len] = util_strdup_s(env);
+    dp->env_len++;
+    return 0;
+}
+
+int defs_process_add_multiple_env(defs_process *dp, const char **envs, size_t env_len)
+{
+    size_t i;
+    char *key = NULL;
+    char *value = NULL;
+    char *saveptr = NULL;
+    __isula_auto_free char *tmp_env = NULL;
+
+    if (envs == NULL || env_len == 0) {
+        DEBUG("empty envs");
+        return 0;
+    }
+    if (dp == NULL) {
+        ERROR("Invalid params");
+        return -1;
+    }
+
+    for (i = 0; i < env_len; i++) {
+        tmp_env = util_strdup_s(envs[i]);
+        key = strtok_r(tmp_env, "=", &saveptr);
+        value = strtok_r(NULL, "=", &saveptr);
+        if (key == NULL || value == NULL) {
+            ERROR("Bad env format: %s", tmp_env);
+            return -1;
+        }
+        if (add_env(dp, envs[i], key) != 0) {
+            return -1;
+        }
+        free(tmp_env);
+        tmp_env = NULL;
+    }
+
+    return 0;
+}
+
+int spec_add_multiple_process_env(oci_runtime_spec *oci_spec, const char **envs, size_t env_len)
+{
+    int ret = 0;
+    
+    if (envs == NULL || env_len == 0) {
+        DEBUG("empty envs");
+        return 0;
+    }
+    if (oci_spec == NULL) {
+        ERROR("Invalid params");
+        return -1;
+    }
+ 
+    ret = make_sure_oci_spec_process(oci_spec);
+    if (ret < 0) {
+        ERROR("Out of memory");
+        return -1;
+    }
+ 
+    ret = defs_process_add_multiple_env(oci_spec->process, envs, env_len);
+    if (ret < 0) {
+        ERROR("Failed to add envs");
+    }
+ 
+    return ret;
+}
+ 
+int spec_add_device(oci_runtime_spec *oci_spec, defs_device *device)
+{
+    int ret = 0;
+    size_t i;
+ 
+    if (device == NULL) {
+        return -1;
+    }
+    ret = make_sure_oci_spec_linux(oci_spec);
+    if (ret < 0) {
+        return -1;
+    }
+    
+    for (i = 0; i < oci_spec->linux->devices_len; i++) {
+        if (strcmp(oci_spec->linux->devices[i]->path, device->path) == 0) {
+            free_defs_device(oci_spec->linux->devices[i]);
+            oci_spec->linux->devices[i] = device;
+            return 0;
+        }
+    }
+
+    if (util_mem_realloc((void **)&oci_spec->linux->devices, (oci_spec->linux->devices_len + 1) * sizeof(char *),
+                         (void *)oci_spec->linux->devices, oci_spec->linux->devices_len * sizeof(char *)) != 0) {
+        ERROR("Out of memory");
+        return -1;
+    }
+    oci_spec->linux->devices[oci_spec->linux->devices_len] = device;
+    oci_spec->linux->devices_len++;
+ 
+    return 0;
+}
+ 
+int spec_add_linux_resources_device(oci_runtime_spec *oci_spec, bool allow, const char *dev_type,
+                                    int64_t major, int64_t minor, const char *access)
+{
+    int ret = 0;
+    defs_device_cgroup *device = NULL;
+ 
+    ret = make_sure_oci_spec_linux_resources(oci_spec);
+    if (ret < 0) {
+        return -1;
+    }
+ 
+    device = util_common_calloc_s(sizeof(*device));
+    if (device == NULL) {
+        ERROR("Out of memory");
+        return -1;
+    }
+    device->allow = allow;
+    device->type = util_strdup_s(dev_type);
+    device->access = util_strdup_s(access);
+    device->major = major;
+    device->minor = minor;
+
+    if (util_mem_realloc((void **)&oci_spec->linux->resources->devices, (oci_spec->linux->resources->devices_len + 1) * sizeof(char *),
+                         (void *)oci_spec->linux->resources->devices, oci_spec->linux->resources->devices_len * sizeof(char *)) != 0) {
+        ERROR("Out of memory");
+        free_defs_device_cgroup(device);
+        return -1;
+    }
+    oci_spec->linux->resources->devices[oci_spec->linux->resources->devices_len] = device;
+    oci_spec->linux->resources->devices_len++;
+ 
+    return 0;
+}
+ 
+void spec_remove_mount(oci_runtime_spec *oci_spec, const char *dest)
+{
+    size_t i;
+ 
+    if (oci_spec == NULL || oci_spec->mounts == NULL || dest == NULL) {
+        return;
+    }
+ 
+    for (i = 0; i < oci_spec->mounts_len; i++) {
+        if (strcmp(oci_spec->mounts[i]->destination, dest) == 0) {
+            free_defs_mount(oci_spec->mounts[i]);
+            (void)memcpy((void **)&oci_spec->mounts[i], (void **)&oci_spec->mounts[i + 1],
+                (oci_spec->mounts_len - i - 1) * sizeof(void *));        
+            oci_spec->mounts_len--;
+            return;
+        }
+    }
+}
+ 
+int spec_add_mount(oci_runtime_spec *oci_spec, defs_mount *mnt)
+{
+    if (oci_spec == NULL || mnt == NULL) {
+        return -1;
+    }
+    
+    if (util_mem_realloc((void **)&oci_spec->mounts, (oci_spec->mounts_len + 1) * sizeof(char *),
+                         (void *)oci_spec->mounts, oci_spec->mounts_len * sizeof(char *)) != 0) {
+        ERROR("Out of memory");
+        return -1;
+    }
+    oci_spec->mounts[oci_spec->mounts_len] = mnt;
+    oci_spec->mounts_len++;
+ 
+    return 0;
+}
+ 
+#define SPEC_ADD_HOOKS_ITEM_DEF(hooktype)                                                                                   \
+    int spec_add_##hooktype##_hook(oci_runtime_spec *oci_spec, defs_hook *hooktype##_hook)                                  \
+    {                                                                                                                       \
+        int ret = 0;                                                                                                        \
+        if (oci_spec == NULL || hooktype##_hook == NULL) {                                                                  \
+            return -1;                                                                                                      \
+        }                                                                                                                   \
+        ret = make_sure_oci_spec_hooks(oci_spec);                                                                           \
+        if (ret < 0) {                                                                                                      \
+            return -1;                                                                                                      \
+        }                                                                                                                   \
+        if (util_mem_realloc((void **)&oci_spec->hooks->hooktype, (oci_spec->hooks->hooktype##_len + 1) * sizeof(char *),   \
+                             (void *)oci_spec->hooks->hooktype, oci_spec->hooks->hooktype##_len * sizeof(char *)) != 0) {   \
+            ERROR("Out of memory");                                                                                         \
+            return -1;                                                                                                      \
+        }                                                                                                                   \
+        oci_spec->hooks->hooktype[oci_spec->hooks->hooktype##_len] = hooktype##_hook;                                       \
+        oci_spec->hooks->hooktype##_len++;                                                                                  \
+        return 0;                                                                                                           \
+    }
+ 
+/* 
+* The OCI being used by the iSulad not supportes 
+* createRuntime/createContainer/startContainer currently.
+*/
+SPEC_ADD_HOOKS_ITEM_DEF(prestart)
+SPEC_ADD_HOOKS_ITEM_DEF(poststart)
+SPEC_ADD_HOOKS_ITEM_DEF(poststop)
+
+#endif /* ENABLE_CDI */
\ No newline at end of file
-- 
2.34.1

