diff options
Diffstat (limited to '0067-cdi-support-modules-container_edits-parser.patch')
-rw-r--r-- | 0067-cdi-support-modules-container_edits-parser.patch | 1210 |
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 + |