summaryrefslogtreecommitdiff
path: root/0067-cdi-support-modules-container_edits-parser.patch
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2024-09-03 03:24:28 +0000
committerCoprDistGit <infra@openeuler.org>2024-09-03 03:24:28 +0000
commite45819fcb4a96649a4030db7684f140d5ca46735 (patch)
tree544dac3e30a0448eabdc50add41aa3a18982d9f1 /0067-cdi-support-modules-container_edits-parser.patch
parent1a71e3afebb4b43be63949dcc8e882fe7643f13b (diff)
automatic import of iSuladopeneuler24.03_LTS
Diffstat (limited to '0067-cdi-support-modules-container_edits-parser.patch')
-rw-r--r--0067-cdi-support-modules-container_edits-parser.patch1210
1 files changed, 1210 insertions, 0 deletions
diff --git a/0067-cdi-support-modules-container_edits-parser.patch b/0067-cdi-support-modules-container_edits-parser.patch
new file mode 100644
index 0000000..901de2b
--- /dev/null
+++ b/0067-cdi-support-modules-container_edits-parser.patch
@@ -0,0 +1,1210 @@
+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
+