lcr/src/conf.c
lifeng dcd14ae6b4 4.24: sync with upstream stable-2.0.0
Signed-off-by: lifeng <lifeng68@huawei.com>
2020-04-24 15:41:00 +08:00

2591 lines
66 KiB
C

/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
* lcr licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
* http://license.coscl.org.cn/MulanPSL
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v1 for more details.
* Author: wujing
* Create: 2018-11-08
* Description: provide container conf function
******************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <linux/oom.h>
#include "conf.h"
#include "lcrcontainer.h"
#include "lcrcontainer_extend.h"
#include "error.h"
#include "utils.h"
#include "log.h"
#include "buffer.h"
#define SUB_UID_PATH "/etc/subuid"
#define SUB_GID_PATH "/etc/subgid"
#define ID_MAP_LEN 100
/* files limit checker */
static int files_limit_checker(const char *value)
{
long long limit = 0;
int ret = 0;
ret = util_safe_llong(value, &limit);
if (ret) {
ret = -1;
}
return ret;
}
/* check console log file */
static int check_console_log_file(const char *value)
{
int ret = 0;
if (value == NULL) {
return -1;
}
if (strcmp(value, "none") == 0) {
ret = 1;
}
return ret;
}
/* check console log filesize */
static int check_console_log_filesize(const char *value)
{
int ret = -1;
int64_t tmp = 0;
int64_t min = 4 * SIZE_KB;
if (value == NULL) {
return ret;
}
if (parse_byte_size_string(value, &tmp) == 0 && tmp >= min) {
ret = 0;
}
return ret;
}
/* check oom score adj */
static int check_oom_score_adj(const char *value)
{
int ret = -1;
int tmp = 0;
int min = OOM_SCORE_ADJ_MIN;
int max = OOM_SCORE_ADJ_MAX;
if (value == NULL) {
return ret;
}
if (util_safe_int(value, &tmp) == 0 && tmp >= min && tmp <= max) {
ret = 0;
}
lcr_set_error_message(LCR_ERR_RUNTIME, "Invalid value %s, range for oom score adj is [%d, %d]", value, min, max);
return ret;
}
/* check console log filerotate */
static int check_console_log_filerotate(const char *value)
{
int ret = -1;
unsigned int tmp = 0;
if (value == NULL) {
return ret;
}
if (util_safe_uint(value, &tmp) == 0) {
ret = 0;
}
return ret;
}
/* check rootfs mount */
static int check_rootfs_mount(const char *value)
{
if (value == NULL) {
return -1;
}
if (!dir_exists(value)) {
lcr_set_error_message(LCR_ERR_RUNTIME, "Container rootfs mount path '%s' is not exist", value);
return -1;
}
return 0;
}
static inline bool is_native_umask_normal(const char *value)
{
return strcmp(value, "normal") == 0;
}
static inline bool is_native_umask_secure(const char *value)
{
return strcmp(value, "secure") == 0;
}
/* check umask */
static int check_native_umask(const char *value)
{
if (value == NULL) {
return -1;
}
if (!is_native_umask_normal(value) && !is_native_umask_secure(value)) {
ERROR("Invalid native umask: %s", value);
return -1;
}
return 0;
}
/* check system container */
static int check_system_container(const char *value)
{
if (value == NULL) {
return -1;
}
if (strcmp(value, "true") != 0) {
ERROR("Invalid system container: %s", value);
return -1;
}
return 0;
}
/* check cgroup dir */
static int check_cgroup_dir(const char *value)
{
if (value == NULL) {
return -1;
}
return 0;
}
static const lcr_annotation_item_t g_require_annotations[] = {
{
"files.limit",
"lxc.cgroup.files.limit",
files_limit_checker,
},
{
"log.console.file",
"lxc.console.logfile",
check_console_log_file,
},
{
"log.console.filesize",
"lxc.console.size",
check_console_log_filesize,
},
{
"log.console.filerotate",
"lxc.console.rotate",
check_console_log_filerotate,
},
{
"rootfs.mount",
"lxc.rootfs.mount",
check_rootfs_mount,
},
{
"cgroup.dir",
"lxc.cgroup.dir",
check_cgroup_dir,
},
{
"native.umask",
"lxc.isulad.umask",
check_native_umask,
},
{
"system.container",
"lxc.isulad.systemd",
check_system_container,
},
{
"proc.oom_score_adj",
"lxc.proc.oom_score_adj",
check_oom_score_adj,
},
};
/* create lcr list node */
struct lcr_list *create_lcr_list_node(const char *key, const char *value)
{
struct lcr_list *node = NULL;
lcr_config_item_t *entry = NULL;
node = util_common_calloc_s(sizeof(*node));
if (node == NULL) {
return NULL;
}
entry = util_common_calloc_s(sizeof(*entry));
if (entry == NULL) {
free(node);
return NULL;
}
entry->name = util_strdup_s(key);
entry->value = util_strdup_s(value);
node->elem = entry;
return node;
}
/* free lcr list node */
void free_lcr_list_node(struct lcr_list *node)
{
lcr_config_item_t *entry = NULL;
if (node == NULL) {
return;
}
entry = node->elem;
if (entry != NULL) {
free(entry->name);
free(entry->value);
}
free(node->elem);
node->elem = NULL;
free(node);
}
/* trans oci hostname */
struct lcr_list *trans_oci_hostname(const char *hostname)
{
if (hostname == NULL) {
return NULL;
}
return create_lcr_list_node("lxc.uts.name", hostname);
}
static bool valid_sep_len(size_t sep_len, size_t len)
{
if (sep_len && (sep_len != 1) && (len > SIZE_MAX / sep_len + 1)) {
return false;
}
return true;
}
/* capabilities join */
static char *capabilities_join(const char *sep, const char **parts, size_t len)
{
char *result = NULL;
size_t sep_len;
size_t result_len;
size_t iter;
sep_len = strlen(sep);
if (valid_sep_len(sep_len, len) == false) {
return NULL;
}
result_len = (len - 1) * sep_len;
/* calculate new string length
* dont calculate `CAP_`
*/
for (iter = 0; iter < len; iter++) {
if (result_len > 4 && (result_len - 4 >= SIZE_MAX - strlen(parts[iter]))) {
return NULL;
}
result_len += strlen(parts[iter]) - 4;
}
result = calloc(result_len + 1, 1);
if (result == NULL) {
return NULL;
}
for (iter = 0; iter < len - 1; iter++) {
(void)strcat(result, &(parts[iter][4]));
(void)strcat(result, sep);
}
(void)strcat(result, &(parts[len - 1][4]));
// Lower case
for (iter = 0; iter < result_len; iter++) {
if (result[iter] >= 'A' && result[iter] <= 'Z') {
result[iter] = (char)(result[iter] + 32);
}
}
return result;
}
#define UID_MAX_SIZE 21
/* UID to use within a private user namespace for init */
static int trans_oci_process_init_uid(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
char buf[UID_MAX_SIZE] = { 0 };
int nret;
int ret = -1;
if (proc->user != NULL && proc->user->uid != INVALID_INT) {
nret = snprintf(buf, sizeof(buf), "%u", (unsigned int)proc->user->uid);
if (nret < 0 || (size_t)nret >= sizeof(buf)) {
goto out;
}
node = create_lcr_list_node("lxc.init.uid", buf);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* GID to use within a private user namespace for init */
static int trans_oci_process_init_gid(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
char buf[UID_MAX_SIZE] = { 0 };
int nret;
int ret = -1;
if (proc->user != NULL && proc->user->gid != INVALID_INT) {
nret = snprintf(buf, sizeof(buf), "%u", (unsigned int)proc->user->gid);
if (nret < 0 || (size_t)nret >= sizeof(buf)) {
goto out;
}
node = create_lcr_list_node("lxc.init.gid", buf);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* additional groups for init command */
static int trans_oci_process_init_groups(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int nret;
size_t i = 0;
int ret = -1;
if (proc->user != NULL && proc->user->additional_gids != NULL && proc->user->additional_gids_len > 0) {
if (proc->user->additional_gids_len > (SIZE_MAX / (LCR_NUMSTRLEN64 + 1))) {
goto out;
}
size_t total_len = (LCR_NUMSTRLEN64 + 1) * proc->user->additional_gids_len;
char *gids = util_common_calloc_s(total_len);
if (gids == NULL) {
goto out;
}
nret = snprintf(gids, total_len, "%u", (unsigned int)(proc->user->additional_gids[0]));
if (nret < 0 || (size_t)nret >= total_len) {
free(gids);
goto out;
}
for (i = 1; i < proc->user->additional_gids_len; i++) {
size_t old_len = strlen(gids);
nret = snprintf(gids + old_len, total_len - old_len, " %u",
(unsigned int)(proc->user->additional_gids[i]));
if (nret < 0 || (size_t)nret >= (total_len - old_len)) {
free(gids);
goto out;
}
}
node = create_lcr_list_node("lxc.isulad.init.groups", gids);
free(gids);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* Sets the command to use as the init system for the containers */
static int trans_oci_process_init_args(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
size_t i = 0;
int ret = -1;
for (i = 0; i < proc->args_len; i++) {
node = create_lcr_list_node("lxc.isulad.init.args", proc->args[i]);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* working directory to use within container */
static int trans_oci_process_init_cwd(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int ret = -1;
if (proc->cwd != NULL) {
node = create_lcr_list_node("lxc.init.cwd", proc->cwd);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* trans oci process init */
static int trans_oci_process_init(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
int ret = -1;
if (trans_oci_process_init_uid(proc, conf)) {
goto out;
}
if (trans_oci_process_init_gid(proc, conf)) {
goto out;
}
if (trans_oci_process_init_groups(proc, conf)) {
goto out;
}
if (trans_oci_process_init_args(proc, conf)) {
goto out;
}
if (trans_oci_process_init_cwd(proc, conf)) {
goto out;
}
ret = 0;
out:
return ret;
}
/* trans oci process env and cap */
static int trans_oci_process_env_and_cap(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
char *boundings = NULL;
int ret = -1;
size_t i;
for (i = 0; i < proc->env_len; i++) {
char *replaced = util_string_replace(" ", SPACE_MAGIC_STR, proc->env[i]);
if (replaced == NULL) {
ERROR("memory allocation error");
goto out;
}
node = create_lcr_list_node("lxc.environment", replaced);
free(replaced);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
if (proc->capabilities != NULL && proc->capabilities->bounding_len > 0) {
boundings =
capabilities_join(" ", (const char **)(proc->capabilities->bounding), proc->capabilities->bounding_len);
if (boundings == NULL) {
ERROR("Failed to join bounding capabilities");
goto out;
}
node = create_lcr_list_node("lxc.cap.keep", boundings);
free(boundings);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
} else {
node = create_lcr_list_node("lxc.cap.keep", "ISULAD_KEEP_NONE");
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* trans oci process prlimit */
static int trans_oci_process_prlimit(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int ret = -1;
int nret;
size_t i;
for (i = 0; i < proc->rlimits_len; i++) {
oci_runtime_spec_process_rlimits_element *lr = proc->rlimits[i];
char buf_key[30] = { 0 };
char buf_value[60] = { 0 };
size_t j;
char *type = util_strdup_s(lr->type);
// Lower case type,eg. RLIMIT_NOFILE -> RLIMIT_nofile
for (j = strlen("RLIMIT_"); j < strlen(type); j++) {
type[j] = (char)tolower(type[j]);
}
// Skip `RLIMIT_`
nret = snprintf(buf_key, sizeof(buf_key), "lxc.prlimit.%s", &(type[7]));
free(type);
if (nret < 0 || (size_t)nret >= sizeof(buf_key)) {
goto out;
}
// We always use format `soft_limit:hard_limit`
nret = snprintf(buf_value, sizeof(buf_value), "%llu:%llu", (unsigned long long)lr->soft,
(unsigned long long)lr->hard);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
goto out;
}
node = create_lcr_list_node(buf_key, buf_value);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* trans oci process no new privs */
static int trans_oci_process_no_new_privs(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int ret = -1;
if (proc->no_new_privileges) {
node = create_lcr_list_node("lxc.no_new_privs", "1");
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
static int trans_oci_process_apparmor(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int ret = -1;
if (proc->apparmor_profile != NULL) {
node = create_lcr_list_node("lxc.aa_profile", proc->apparmor_profile);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
static int trans_oci_process_selinux(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int ret = -1;
if (proc->selinux_label != NULL) {
node = create_lcr_list_node("lxc.selinux.context", proc->selinux_label);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* trans oci process apparmor and selinux */
static int trans_oci_process_apparmor_and_selinux(const oci_runtime_spec_process *proc, struct lcr_list *conf)
{
int ret = -1;
if (trans_oci_process_apparmor(proc, conf) != 0) {
goto out;
}
if (trans_oci_process_selinux(proc, conf) != 0) {
goto out;
}
ret = 0;
out:
return ret;
}
/* trans oci process */
struct lcr_list *trans_oci_process(const oci_runtime_spec_process *proc)
{
struct lcr_list *conf = NULL;
conf = util_common_calloc_s(sizeof(struct lcr_list));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
if (trans_oci_process_init(proc, conf)) {
goto out_free;
}
if (trans_oci_process_env_and_cap(proc, conf)) {
goto out_free;
}
if (trans_oci_process_prlimit(proc, conf)) {
goto out_free;
}
if (trans_oci_process_no_new_privs(proc, conf)) {
goto out_free;
}
if (trans_oci_process_apparmor_and_selinux(proc, conf)) {
goto out_free;
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
#define APPEND_COMMA_END_SIZE 2
/* trans oci root rootfs */
static int trans_oci_root_rootfs(const oci_runtime_spec_root *root, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int ret = -1;
if ((root != NULL) && root->path != NULL) {
if (strcmp(root->path, "/") != 0) {
node = create_lcr_list_node("lxc.rootfs.path", root->path);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
}
ret = 0;
out:
return ret;
}
static inline bool is_root_readonly(const oci_runtime_spec_root *root)
{
return root != NULL && root->readonly;
}
/* trans oci root rootfsoptions */
static int trans_oci_root_rootfs_options(const oci_runtime_spec_root *root, struct lcr_list *conf,
const oci_runtime_config_linux *linux)
{
struct lcr_list *node = NULL;
char *value = NULL;
char *tmpvalue = NULL;
int ret = -1;
int nret;
if (is_root_readonly(root)) {
value = util_strdup_s("ro");
}
if ((linux != NULL) && linux->rootfs_propagation != NULL) {
if (value != NULL) {
size_t newsize;
if (strlen(value) > (SIZE_MAX - strlen(linux->rootfs_propagation)) - APPEND_COMMA_END_SIZE) {
ERROR("Out of range!");
goto out;
}
newsize = strlen(linux->rootfs_propagation) + strlen(value) + APPEND_COMMA_END_SIZE;
nret = mem_realloc((void **)&tmpvalue, newsize, value, strlen(value));
if (nret < 0) {
ERROR("Out of memory");
goto out;
}
value = tmpvalue;
nret = snprintf(value + strlen(value), newsize - strlen(value), ",%s", linux->rootfs_propagation);
if (nret < 0 || (size_t)nret >= (newsize - strlen(value))) {
ERROR("Failed to print string");
goto out;
}
} else {
value = util_strdup_s(linux->rootfs_propagation);
}
}
if (value != NULL) {
node = create_lcr_list_node("lxc.rootfs.options", value);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
free(value);
return ret;
}
/* trans oci root */
struct lcr_list *trans_oci_root(const oci_runtime_spec_root *root, const oci_runtime_config_linux *linux)
{
struct lcr_list *conf = NULL;
conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
if (trans_oci_root_rootfs(root, conf)) {
goto out_free;
}
if (trans_oci_root_rootfs_options(root, conf, linux)) {
goto out_free;
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
static inline bool is_mount_options_invalid(const defs_mount *mount)
{
return mount == NULL || mount->type == NULL;
}
static inline bool is_mount_type_bind(const char *type)
{
return strcmp(type, "bind") == 0;
}
static inline bool is_mount_type_cgroup(const char *type)
{
return strcmp(type, "cgroup") == 0;
}
static inline bool is_mount_type_sysfs(const char *type)
{
return strcmp(type, "sysfs") == 0;
}
static inline bool is_mount_type_proc(const char *type)
{
return strcmp(type, "proc") == 0;
}
/* trans mount to lxc options */
static char *trans_mount_to_lxc_options(const defs_mount *mount)
{
char *result = NULL;
char *prefix = NULL;
char *lxc_options = NULL;
int rc;
bool isdir = true;
struct stat st;
if (is_mount_options_invalid(mount)) {
return NULL;
}
if (is_mount_type_bind(mount->type)) {
rc = stat(mount->source, &st);
if (rc != 0) {
ERROR("Failed to get stat of %s", mount->source);
goto free_out;
}
isdir = S_ISDIR(st.st_mode);
}
lxc_options = isdir ? ",create=dir" : ",create=file";
prefix = util_string_join(",", (const char **)mount->options, mount->options_len);
if (prefix == NULL) {
prefix = util_strdup_s("defaults");
}
result = util_string_append(lxc_options, prefix);
free(prefix);
return result;
free_out:
free(prefix);
free(result);
return NULL;
}
static bool get_mount_option_ro(const defs_mount *mount)
{
bool ro = false;
if ((mount->options != NULL) && mount->options_len) {
size_t i = 0;
for (i = 0; i < mount->options_len; i++) {
if ((mount->options[i] != NULL) && !strcmp(mount->options[i], "ro")) {
ro = true;
break;
}
}
}
return ro;
}
static char *get_mount_readmode_options(const defs_mount *mount, const char *type)
{
char *options = NULL;
bool readonly = get_mount_option_ro(mount);
if (is_mount_type_cgroup(type)) {
if (readonly) {
options = util_strdup_s("ro:force");
} else {
options = util_strdup_s("rw:force");
}
} else {
if (readonly) {
options = util_strdup_s("ro");
} else {
options = util_strdup_s("rw");
}
}
return options;
}
/* trans mount auto to lxc */
static struct lcr_list *trans_mount_auto_to_lxc(const defs_mount *mount)
{
struct lcr_list *node = NULL;
size_t buf_len = 0;
char *buf = NULL;
char *options = NULL;
int ret;
char *type = NULL;
if (is_mount_options_invalid(mount)) {
ERROR("oci container mounts element(type) is empty");
return NULL;
}
type = mount->type;
if (is_mount_type_sysfs(type)) {
type = "sys";
}
options = get_mount_readmode_options(mount, type);
if (options == NULL) {
ERROR("Failed to trans to lxc options");
goto out_free;
}
buf_len = strlen(type) + strlen(options) + 2;
buf = calloc(buf_len, 1);
if (buf == NULL) {
DEBUG("Out of memory");
goto out_free;
}
ret = snprintf(buf, buf_len, "%s:%s", type, options);
if (ret < 0 || (size_t)ret >= buf_len) {
DEBUG("Failed to print string");
goto out_free;
}
node = create_lcr_list_node("lxc.mount.auto", buf);
out_free:
free(options);
free(buf);
return node;
}
/* trans mount entry to lxc */
static struct lcr_list *trans_mount_entry_to_lxc(const defs_mount *mount)
{
struct lcr_list *node = NULL;
size_t buf_len = 0;
char *buf = NULL;
char *options = NULL;
char *replaced_dest = NULL;
int ret;
char *replaced_source = util_string_replace(" ", SPACE_MAGIC_STR, mount->source);
if (replaced_source == NULL) {
ERROR("memory allocation error");
goto err_out;
}
replaced_dest = util_string_replace(" ", SPACE_MAGIC_STR, mount->destination);
if (replaced_dest == NULL) {
ERROR("memory allocation error");
free(replaced_source);
goto err_out;
}
options = trans_mount_to_lxc_options(mount);
if (options == NULL) {
ERROR("Failed to trans to lxc options");
goto out_free;
}
buf_len = strlen(replaced_dest) + strlen(mount->type) + strlen(replaced_source) + strlen(options) + 8;
buf = calloc(buf_len, 1);
if (buf == NULL) {
ERROR("Out of memory");
goto out_free;
}
ret = snprintf(buf, buf_len, "%s %s %s %s 0 0", replaced_source, replaced_dest + 1, mount->type, options);
if (ret < 0 || (size_t)ret >= buf_len) {
ERROR("Failed to print string");
goto out_free;
}
node = create_lcr_list_node("lxc.mount.entry", buf);
out_free:
free(options);
free(buf);
free(replaced_source);
free(replaced_dest);
err_out:
return node;
}
bool is_system_container(const oci_runtime_spec *container)
{
size_t i = 0;
for (i = 0; container->annotations != NULL && i < container->annotations->len; i++) {
if (strcmp(container->annotations->keys[i], "system.container") == 0) {
return true;
}
}
return false;
}
static bool is_external_rootfs(const oci_runtime_spec *container)
{
size_t i = 0;
for (i = 0; container->annotations != NULL && i < container->annotations->len; i++) {
if (strcmp(container->annotations->keys[i], "external.rootfs") == 0) {
return true;
}
}
return false;
}
static struct lcr_list *trans_oci_mounts_normal(const defs_mount *tmp)
{
struct lcr_list *node = NULL;
if (is_mount_type_cgroup(tmp->type) || is_mount_type_proc(tmp->type) || is_mount_type_sysfs(tmp->type)) {
node = trans_mount_auto_to_lxc(tmp);
} else {
node = trans_mount_entry_to_lxc(tmp);
}
return node;
}
static struct lcr_list *trans_oci_mounts_system_container(const defs_mount *tmp)
{
struct lcr_list *node = NULL;
if (is_mount_type_cgroup(tmp->type) || (is_mount_type_proc(tmp->source) && is_mount_type_proc(tmp->type)) ||
is_mount_type_sysfs(tmp->type)) {
node = trans_mount_auto_to_lxc(tmp);
} else {
node = trans_mount_entry_to_lxc(tmp);
}
return node;
}
static struct lcr_list *trans_oci_mounts_node(const oci_runtime_spec *c, const defs_mount *tmp)
{
struct lcr_list *node = NULL;
// system container
if (is_system_container(c)) {
node = trans_oci_mounts_system_container(tmp);
} else {
node = trans_oci_mounts_normal(tmp);
}
return node;
}
static inline bool is_mount_destination_dev(const char *destination)
{
return destination != NULL && strcmp(destination, "/dev") == 0;
}
static inline bool should_ignore_dev_mount(const defs_mount *tmp, bool system_container, bool external_rootfs)
{
return system_container && external_rootfs && is_mount_destination_dev(tmp->destination);
}
/* trans oci mounts */
struct lcr_list *trans_oci_mounts(const oci_runtime_spec *c)
{
struct lcr_list *conf = NULL;
struct lcr_list *node = NULL;
defs_mount *tmp = NULL;
size_t i;
bool system_container = is_system_container(c);
bool external_rootfs = is_external_rootfs(c);
conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
for (i = 0; i < c->mounts_len; i++) {
tmp = c->mounts[i];
if (tmp->type == NULL) {
goto out_free;
}
if (should_ignore_dev_mount(tmp, system_container, external_rootfs)) {
continue;
}
node = trans_oci_mounts_node(c, tmp);
if (node == NULL) {
goto out_free;
}
lcr_list_add_tail(conf, node);
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
static int trans_one_oci_id_mapping(struct lcr_list *conf, const char *typ, const defs_id_mapping *id, const char *path)
{
int nret;
struct lcr_list *node = NULL;
char buf_value[300] = { 0 };
char subid[ID_MAP_LEN] = { 0 };
nret = snprintf(buf_value, sizeof(buf_value), "%s %u %u %u", typ, id->container_id, id->host_id, id->size);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
return -1;
}
node = create_lcr_list_node("lxc.idmap", buf_value);
if (node == NULL) {
return -1;
}
lcr_list_add_tail(conf, node);
nret = snprintf(subid, sizeof(subid), "%u:%u:%u", id->container_id, id->host_id, id->size);
if (nret < 0 || (size_t)nret >= sizeof(subid)) {
return -1;
}
nret = util_atomic_write_file(path, subid);
if (nret < 0) {
return -1;
}
return 0;
}
static int trans_oci_uid_mapping(struct lcr_list *conf, defs_id_mapping **uid_mappings, size_t uid_mappings_len)
{
size_t i;
for (i = 0; uid_mappings != NULL && i < uid_mappings_len; i++) {
int nret = trans_one_oci_id_mapping(conf, "u", uid_mappings[i], SUB_UID_PATH);
if (nret < 0) {
return nret;
}
}
return 0;
}
static int trans_oci_gid_mapping(struct lcr_list *conf, defs_id_mapping **gid_mappings, size_t gid_mappings_len)
{
size_t i;
for (i = 0; gid_mappings != NULL && i < gid_mappings_len; i++) {
int nret = trans_one_oci_id_mapping(conf, "g", gid_mappings[i], SUB_GID_PATH);
if (nret < 0) {
return nret;
}
}
return 0;
}
/* trans oci id mapping */
static struct lcr_list *trans_oci_id_mapping(const oci_runtime_config_linux *l)
{
struct lcr_list *conf = NULL;
int nret = 0;
conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
nret = trans_oci_uid_mapping(conf, l->uid_mappings, l->uid_mappings_len);
if (nret < 0) {
goto out_free;
}
nret = trans_oci_gid_mapping(conf, l->gid_mappings, l->gid_mappings_len);
if (nret < 0) {
goto out_free;
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
#define WILDCARD (-1LL)
static int trans_conf_int(struct lcr_list *conf, const char *lxc_key, int val)
{
struct lcr_list *node = NULL;
char buf_value[300] = { 0 };
int nret;
nret = snprintf(buf_value, sizeof(buf_value), "%d", val);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
return -1;
}
node = create_lcr_list_node(lxc_key, buf_value);
if (node == NULL) {
return -1;
}
lcr_list_add_tail(conf, node);
return 0;
}
static int trans_conf_uint32(struct lcr_list *conf, const char *lxc_key, uint32_t val)
{
struct lcr_list *node = NULL;
char buf_value[300] = { 0 };
int nret;
nret = snprintf(buf_value, sizeof(buf_value), "%u", (unsigned int)val);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
return -1;
}
node = create_lcr_list_node(lxc_key, buf_value);
if (node == NULL) {
return -1;
}
lcr_list_add_tail(conf, node);
return 0;
}
static int trans_conf_int64(struct lcr_list *conf, const char *lxc_key, int64_t val)
{
struct lcr_list *node = NULL;
char buf_value[300] = { 0 };
int nret;
nret = snprintf(buf_value, sizeof(buf_value), "%lld", (long long)val);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
return -1;
}
node = create_lcr_list_node(lxc_key, buf_value);
if (node == NULL) {
return -1;
}
lcr_list_add_tail(conf, node);
return 0;
}
static int trans_conf_uint64(struct lcr_list *conf, const char *lxc_key, uint64_t val)
{
struct lcr_list *node = NULL;
char buf_value[300] = { 0 };
int nret;
nret = snprintf(buf_value, sizeof(buf_value), "%llu", (unsigned long long)val);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
return -1;
}
node = create_lcr_list_node(lxc_key, buf_value);
if (node == NULL) {
return -1;
}
lcr_list_add_tail(conf, node);
return 0;
}
/* trans resources mem swap */
static int trans_resources_mem_swap(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
int nret;
if (res->memory->reservation != INVALID_INT) {
/* set soft limit of memory usage */
nret = trans_conf_int64(conf, "lxc.cgroup.memory.soft_limit_in_bytes", res->memory->reservation);
if (nret < 0) {
goto out;
}
}
if (res->memory->swap != INVALID_INT) {
/* set limit of memory+swap usage */
nret = trans_conf_int64(conf, "lxc.cgroup.memory.memsw.limit_in_bytes", res->memory->swap);
if (nret < 0) {
goto out;
}
}
if (res->memory->swappiness != -1) {
/* set swappiness parameter of vmscan */
nret = trans_conf_uint64(conf, "lxc.cgroup.memory.swappiness", res->memory->swappiness);
if (nret < 0) {
goto out;
}
}
ret = 0;
out:
return ret;
}
static int trans_resources_mem_limit(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
if (res->memory->limit != INVALID_INT) {
/* set limit of memory usage */
int nret = trans_conf_int64(conf, "lxc.cgroup.memory.limit_in_bytes", res->memory->limit);
if (nret < 0) {
return -1;
}
}
return 0;
}
/* trans resources mem kernel */
static int trans_resources_mem_kernel(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
int nret;
if (res->memory->kernel != INVALID_INT) {
/* set hard limit for kernel memory */
nret = trans_conf_int64(conf, "lxc.cgroup.memory.kmem.limit_in_bytes", res->memory->kernel);
if (nret < 0) {
goto out;
}
}
if (res->memory->kernel_tcp != INVALID_INT) {
/* set hard limit for tcp buf memory */
nret = trans_conf_int64(conf, "lxc.cgroup.memory.kmem.tcp.limit_in_bytes", res->memory->kernel_tcp);
if (nret < 0) {
goto out;
}
}
ret = 0;
out:
return ret;
}
static int trans_resources_mem_disable_oom(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
if (res->memory->disable_oom_killer) {
node = create_lcr_list_node("lxc.cgroup.memory.oom_control", "1");
if (node == NULL) {
return -1;
}
lcr_list_add_tail(conf, node);
}
return 0;
}
/* trans resources memory */
static int trans_resources_memory(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
if (res->memory == NULL) {
return 0;
}
if (trans_resources_mem_limit(res, conf) != 0) {
goto out;
}
if (trans_resources_mem_swap(res, conf) != 0) {
goto out;
}
if (trans_resources_mem_kernel(res, conf) != 0) {
goto out;
}
if (trans_resources_mem_disable_oom(res, conf) != 0) {
goto out;
}
ret = 0;
out:
return ret;
}
static int trans_resources_devices_node(const oci_runtime_defs_linux_device_cgroup *lrd, struct lcr_list *conf,
const char *buf_value)
{
struct lcr_list *node = NULL;
int ret = -1;
if (lrd->allow == true) {
node = create_lcr_list_node("lxc.cgroup.devices.allow", buf_value);
} else {
node = create_lcr_list_node("lxc.cgroup.devices.deny", buf_value);
}
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
ret = 0;
out:
return ret;
}
static int trans_resources_devices_no_match(const oci_runtime_defs_linux_device_cgroup *lrd, char *buf_value,
size_t size)
{
int ret = 0;
if (lrd->minor != WILDCARD) {
ret = snprintf(buf_value, size, "%s %lld:%lld %s", lrd->type ? lrd->type : "a", (long long)(lrd->major),
(long long)lrd->minor, lrd->access ? lrd->access : "rwm");
} else {
ret = snprintf(buf_value, size, "%s %lld:* %s", lrd->type ? lrd->type : "a", (long long)(lrd->major),
lrd->access ? lrd->access : "rwm");
}
return ret;
}
static int trans_resources_devices_match(const oci_runtime_defs_linux_device_cgroup *lrd, char *buf_value, size_t size)
{
int ret = 0;
if (lrd->minor != WILDCARD) {
ret = snprintf(buf_value, size, "%s *:%lld %s", lrd->type ? lrd->type : "a", (long long)(lrd->minor),
lrd->access ? lrd->access : "rwm");
} else {
ret = snprintf(buf_value, size, "%s *:* %s", lrd->type ? lrd->type : "a", lrd->access ? lrd->access : "rwm");
}
return ret;
}
static int trans_resources_devices_ret(const oci_runtime_defs_linux_device_cgroup *lrd, char *buf_value, size_t size)
{
int ret = 0;
if (lrd->major != WILDCARD) {
ret = trans_resources_devices_no_match(lrd, buf_value, size);
} else {
ret = trans_resources_devices_match(lrd, buf_value, size);
}
return ret;
}
/* trans resources devices */
static int trans_resources_devices(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
size_t i = 0;
char buf_value[300] = { 0 };
for (i = 0; i < res->devices_len; i++) {
oci_runtime_defs_linux_device_cgroup *lrd = res->devices[i];
if (trans_resources_devices_ret(lrd, buf_value, sizeof(buf_value)) < 0) {
goto out;
}
if (trans_resources_devices_node(lrd, conf, buf_value) < 0) {
goto out;
}
}
ret = 0;
out:
return ret;
}
/* trans resources cpu cfs */
static int trans_resources_cpu_cfs(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
if (res->cpu->quota != INVALID_INT) {
if (trans_conf_int64(conf, "lxc.cgroup.cpu.cfs_quota_us", res->cpu->quota) < 0) {
goto out;
}
}
if (res->cpu->period != INVALID_INT) {
if (trans_conf_uint64(conf, "lxc.cgroup.cpu.cfs_period_us", res->cpu->period) < 0) {
goto out;
}
}
ret = 0;
out:
return ret;
}
/* trans resources cpu rt */
static int trans_resources_cpu_rt(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
if (res->cpu->realtime_runtime != INVALID_INT) {
if (trans_conf_int64(conf, "lxc.cgroup.cpu.rt_runtime_us", res->cpu->realtime_runtime) < 0) {
goto out;
}
}
if (res->cpu->realtime_period != INVALID_INT) {
if (trans_conf_uint64(conf, "lxc.cgroup.cpu.rt_period_us", res->cpu->realtime_period) < 0) {
goto out;
}
}
ret = 0;
out:
return ret;
}
/* trans resources cpu set */
static int trans_resources_cpu_set(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int ret = -1;
if (res->cpu->cpus != NULL) {
node = create_lcr_list_node("lxc.cgroup.cpuset.cpus", res->cpu->cpus);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
if (res->cpu->mems != NULL) {
node = create_lcr_list_node("lxc.cgroup.cpuset.mems", res->cpu->mems);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* trans resources cpu shares */
static int trans_resources_cpu_shares(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
if (res->cpu->shares != INVALID_INT) {
int nret = trans_conf_int64(conf, "lxc.cgroup.cpu.shares", (int64_t)(res->cpu->shares));
if (nret < 0) {
return -1;
}
}
return 0;
}
/* trans resources cpu */
static int trans_resources_cpu(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
if (res->cpu == NULL) {
return 0;
}
if (trans_resources_cpu_cfs(res, conf)) {
goto out;
}
if (trans_resources_cpu_rt(res, conf)) {
goto out;
}
if (trans_resources_cpu_set(res, conf)) {
goto out;
}
if (trans_resources_cpu_shares(res, conf)) {
goto out;
}
ret = 0;
out:
return ret;
}
/* trans resources blkio weight */
static int trans_blkio_weight(const oci_runtime_config_linux_resources_block_io *block_io, struct lcr_list *conf)
{
int ret = -1;
if (block_io->weight != INVALID_INT) {
if (trans_conf_int(conf, "lxc.cgroup.blkio.weight", block_io->weight) < 0) {
goto out;
}
}
if (block_io->leaf_weight != INVALID_INT) {
if (trans_conf_int(conf, "lxc.cgroup.blkio.leaf_weight", block_io->leaf_weight) < 0) {
goto out;
}
}
ret = 0;
out:
return ret;
}
/* trans resources blkio wdevice */
static int trans_blkio_wdevice(const oci_runtime_config_linux_resources_block_io *block_io, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int ret = -1;
size_t i = 0;
char buf_value[300] = { 0 };
for (i = 0; i < block_io->weight_device_len; i++) {
int nret;
oci_runtime_defs_linux_block_io_device_weight *wd = block_io->weight_device[i];
if ((wd != NULL) && wd->weight != INVALID_INT) {
nret = snprintf(buf_value, sizeof(buf_value), "%lld:%lld %d", (long long)(wd->major), (long long)wd->minor,
wd->weight);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
goto out;
}
node = create_lcr_list_node("lxc.cgroup.blkio.weight_device", buf_value);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
if ((wd != NULL) && wd->leaf_weight != INVALID_INT) {
nret = snprintf(buf_value, sizeof(buf_value), "%lld:%lld %d", (long long)(wd->major),
(long long)(wd->minor), wd->leaf_weight);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
goto out;
}
node = create_lcr_list_node("lxc.cgroup.blkio.leaf_weight_device", buf_value);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
}
ret = 0;
out:
return ret;
}
/* trans resources blkio throttle */
static int trans_blkio_throttle(oci_runtime_defs_linux_block_io_device_throttle **throttle, size_t len,
const char *lxc_key, struct lcr_list *conf)
{
struct lcr_list *node = NULL;
int ret = -1;
size_t i;
if ((throttle == NULL) || len == 0) {
return 0;
}
for (i = 0; i < len; i++) {
if (throttle[i] && throttle[i]->rate != INVALID_INT) {
int nret;
char buf_value[300] = { 0x00 };
nret = snprintf(buf_value, sizeof(buf_value), "%lld:%lld %llu", (long long)throttle[i]->major,
(long long)(throttle[i]->minor), (unsigned long long)(throttle[i]->rate));
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
goto out;
}
node = create_lcr_list_node(lxc_key, buf_value);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
}
ret = 0;
out:
return ret;
}
/* trans resources blkio */
static int trans_resources_blkio(const oci_runtime_config_linux_resources_block_io *block_io, struct lcr_list *conf)
{
int ret = -1;
if (block_io == NULL) {
return 0;
}
if (trans_blkio_weight(block_io, conf)) {
goto out;
}
if (trans_blkio_wdevice(block_io, conf)) {
goto out;
}
if (trans_blkio_throttle(block_io->throttle_read_bps_device, block_io->throttle_read_bps_device_len,
"lxc.cgroup.blkio.throttle.read_bps_device", conf)) {
goto out;
}
if (trans_blkio_throttle(block_io->throttle_write_bps_device, block_io->throttle_write_bps_device_len,
"lxc.cgroup.blkio.throttle.write_bps_device", conf)) {
goto out;
}
if (trans_blkio_throttle(block_io->throttle_read_iops_device, block_io->throttle_read_iops_device_len,
"lxc.cgroup.blkio.throttle.read_iops_device", conf)) {
goto out;
}
if (trans_blkio_throttle(block_io->throttle_write_iops_device, block_io->throttle_write_iops_device_len,
"lxc.cgroup.blkio.throttle.write_iops_device", conf)) {
goto out;
}
ret = 0;
out:
return ret;
}
/* trans resources hugetlb */
static int trans_resources_hugetlb(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
size_t i = 0;
char buf_key[300] = { 0 };
for (i = 0; i < res->hugepage_limits_len; i++) {
oci_runtime_config_linux_resources_hugepage_limits_element *lrhl = res->hugepage_limits[i];
if (lrhl->page_size != NULL) {
int nret = snprintf(buf_key, sizeof(buf_key), "lxc.cgroup.hugetlb.%s.limit_in_bytes", lrhl->page_size);
if (nret < 0 || (size_t)nret >= sizeof(buf_key)) {
goto out;
}
if (trans_conf_uint64(conf, buf_key, lrhl->limit) < 0) {
return -1;
}
}
}
ret = 0;
out:
return ret;
}
/* trans resources network */
static int trans_resources_network(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
size_t i = 0;
char buf_value[300] = { 0 };
if (!res->network) {
return 0;
}
if (res->network->class_id != INVALID_INT) {
if (trans_conf_uint32(conf, "lxc.cgroup.net_cls.classid", res->network->class_id) < 0) {
return -1;
}
}
for (i = 0; i < res->network->priorities_len; i++) {
oci_runtime_defs_linux_network_interface_priority *lrnp = res->network->priorities[i];
if ((lrnp != NULL) && lrnp->name != NULL && lrnp->priority != INVALID_INT) {
int nret = snprintf(buf_value, sizeof(buf_value), "%s %u", lrnp->name, lrnp->priority);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
goto out;
}
struct lcr_list *node = create_lcr_list_node("lxc.cgroup.net_prio.ifpriomap", buf_value);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
}
ret = 0;
out:
return ret;
}
/* trans resources pids */
static int trans_resources_pids(const oci_runtime_config_linux_resources *res, struct lcr_list *conf)
{
int ret = -1;
char buf_value[300] = { 0 };
if (res->pids == NULL) {
return 0;
}
if (res->pids->limit != INVALID_INT) {
int nret;
if (res->pids->limit == -1) {
nret = snprintf(buf_value, sizeof(buf_value), "max");
} else {
nret = snprintf(buf_value, sizeof(buf_value), "%lld", (long long)(res->pids->limit));
}
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
goto out;
}
struct lcr_list *node = create_lcr_list_node("lxc.cgroup.pids.max", buf_value);
if (node == NULL) {
goto out;
}
lcr_list_add_tail(conf, node);
}
ret = 0;
out:
return ret;
}
/* trans oci resources */
static struct lcr_list *trans_oci_resources(const oci_runtime_config_linux_resources *res)
{
struct lcr_list *conf = NULL;
conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
if (trans_resources_devices(res, conf)) {
goto out_free;
}
if (trans_resources_memory(res, conf)) {
goto out_free;
}
if (trans_resources_cpu(res, conf)) {
goto out_free;
}
if (trans_resources_blkio(res->block_io, conf)) {
goto out_free;
}
if (trans_resources_hugetlb(res, conf)) {
goto out_free;
}
if (trans_resources_network(res, conf)) {
goto out_free;
}
if (trans_resources_pids(res, conf)) {
goto out_free;
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
struct namespace_map_def {
char *ns_name;
char *lxc_name;
};
static char *trans_oci_namespace_to_lxc(const char *typ)
{
struct namespace_map_def namespaces_map[] = {
{ "pid", "lxc.namespace.share.pid" }, { "network", "lxc.namespace.share.net" },
{ "ipc", "lxc.namespace.share.ipc" }, { "uts", "lxc.namespace.share.uts" },
{ "mount", "lxc.namespace.share.mnt" }, { "user", "lxc.namespace.share.user" },
{ "cgroup", "lxc.namespace.share.cgroup" }, { NULL, NULL }
};
const struct namespace_map_def *p = NULL;
for (p = namespaces_map; p != NULL && p->ns_name != NULL; p++) {
if (strcmp(typ, p->ns_name) == 0) {
return util_strdup_s(p->lxc_name);
}
}
return NULL;
}
/* trans oci namespaces */
static struct lcr_list *trans_oci_namespaces(const oci_runtime_config_linux *l)
{
struct lcr_list *conf = NULL;
struct lcr_list *node = NULL;
size_t i;
oci_runtime_defs_linux_namespace_reference *ns = NULL;
conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
for (i = 0; i < l->namespaces_len; i++) {
char *ns_name = NULL;
ns = l->namespaces[i];
if (ns->type == NULL || ns->path == NULL) {
continue;
}
ns_name = trans_oci_namespace_to_lxc(ns->type);
if (ns_name == NULL) {
continue;
}
node = create_lcr_list_node(ns_name, ns->path);
free(ns_name);
if (node == NULL) {
goto out_free;
}
lcr_list_add_tail(conf, node);
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
/* trans oci mask ro paths */
static struct lcr_list *trans_oci_mask_ro_paths(const oci_runtime_config_linux *l)
{
struct lcr_list *conf = NULL;
struct lcr_list *node = NULL;
size_t i;
char *path = NULL;
conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
for (i = 0; i < l->masked_paths_len; i++) {
path = l->masked_paths[i];
if (path == NULL) {
continue;
}
node = create_lcr_list_node("lxc.isulad.rootfs.maskedpaths", path);
if (node == NULL) {
goto out_free;
}
lcr_list_add_tail(conf, node);
}
for (i = 0; i < l->readonly_paths_len; i++) {
path = l->readonly_paths[i];
if (path == NULL) {
continue;
}
node = create_lcr_list_node("lxc.isulad.rootfs.ropaths", path);
if (node == NULL) {
goto out_free;
}
lcr_list_add_tail(conf, node);
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
#define POPULATE_DEVICE_SIZE (300 + PATH_MAX)
/* trans oci linux devices */
static struct lcr_list *trans_oci_linux_devices(const oci_runtime_config_linux *l)
{
struct lcr_list *conf = NULL;
struct lcr_list *node = NULL;
size_t i = 0;
int nret = 0;
oci_runtime_defs_linux_device *device = NULL;
char buf_value[POPULATE_DEVICE_SIZE] = { 0 };
conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
for (i = 0; i < l->devices_len; i++) {
device = l->devices[i];
if (device->type == NULL || device->path == NULL) {
continue;
}
/* lxc.populate_device = PATH_IN_CONTAINER:DEVICETYPE:MAJOR:MINOR:MODE:UID:GID
* For e.g. lxc.populate_device = /dev/sda:b:8:0:0666:0:0
*/
nret = snprintf(buf_value, sizeof(buf_value), "%s:%s:%lld:%lld:%d:%u:%u", device->path, device->type,
(long long int)(device->major), (long long int)(device->minor), device->file_mode, device->uid,
device->gid);
if (nret < 0 || (size_t)nret >= sizeof(buf_value)) {
ERROR("Failed to get populate device string");
goto out_free;
}
node = create_lcr_list_node("lxc.isulad.populate.device", buf_value);
if (node == NULL) {
goto out_free;
}
lcr_list_add_tail(conf, node);
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
static inline bool is_seccomp_action_kill(const char *value)
{
return strcmp(value, "SCMP_ACT_KILL") == 0;
}
static inline bool is_seccomp_action_trap(const char *value)
{
return strcmp(value, "SCMP_ACT_TRAP") == 0;
}
static inline bool is_seccomp_action_allow(const char *value)
{
return strcmp(value, "SCMP_ACT_ALLOW") == 0;
}
static inline bool is_seccomp_action_trace(const char *value)
{
return strcmp(value, "SCMP_ACT_TRACE") == 0;
}
static inline bool is_seccomp_action_errno(const char *value)
{
return strcmp(value, "SCMP_ACT_ERRNO") == 0;
}
/* seccomp trans action */
static char *seccomp_trans_action(const char *action)
{
if (is_seccomp_action_kill(action)) {
return util_strdup_s("kill");
} else if (is_seccomp_action_trap(action)) {
return util_strdup_s("trap");
} else if (is_seccomp_action_allow(action)) {
return util_strdup_s("allow");
} else if (is_seccomp_action_trace(action)) {
return util_strdup_s("trace 1");
} else if (is_seccomp_action_errno(action)) {
return util_strdup_s("errno 1");
}
return NULL;
}
static bool is_action_allow(const char *value)
{
return strcmp(value, "allow") == 0;
}
#define DEFAULT_ACTION_OFFSET 12
/* seccomp append head info */
static int seccomp_append_head_info(const char *action, Buffer *buffer)
{
int ret = 0;
char *default_action = NULL;
if (action == NULL) {
return -1;
}
default_action = seccomp_trans_action(action);
if (default_action == NULL) {
ERROR("Failed to translate seccomp action");
return -1;
}
if (is_action_allow(default_action)) {
ret = buffer_nappendf(buffer, strlen(default_action) + DEFAULT_ACTION_OFFSET, "blacklist %s\n", default_action);
} else {
ret = buffer_nappendf(buffer, strlen(default_action) + DEFAULT_ACTION_OFFSET, "whitelist %s\n", default_action);
}
if (ret != 0) {
ERROR("Failed to append seccomp config head info\n");
ret = -1;
goto out;
}
out:
free(default_action);
return ret;
}
/* get hostarch */
static char *get_hostarch(void)
{
struct utsname uts;
size_t len;
size_t i;
/* no x32 kernels */
lcr_host_arch arch_type[] = {
{ "i686", "[x86]", 4 }, { "x32", "[x32]", 3 }, { "x86_64", "[x86_64]", 6 },
{ "armv7", "[arm]", 5 }, { "aarch64", "[arm64]", 7 }, { "ppc64le", "[ppc64le]", 7 },
{ "ppc64", "[ppc64]", 5 }, { "ppc", "[ppc]", 3 }, { "mips64n32", "[mips64n32]", 9 },
{ "mips64", "[mips64]", 6 }, { "mips", "[mips]", 4 }, { "s390x", "[s390x]", 5 },
{ "s390", "[s390]", 4 }, { "parisc64", "[parisc64]", 8 }, { "parisc", "[parisc]", 6 },
};
if (uname(&uts) < 0) {
SYSERROR("Failed to read host arch");
return NULL;
}
len = sizeof(arch_type) / sizeof(lcr_host_arch);
for (i = 0; i < len; i++) {
if (i == 0 || i == 1 || i == 2) {
if (strcmp(uts.machine, arch_type[i].arch) == 0) {
return util_strdup_s(arch_type[i].value);
}
} else {
if (strncmp(uts.machine, arch_type[i].arch, (size_t)(arch_type[i].num)) == 0) {
return util_strdup_s(arch_type[i].value);
}
}
}
ERROR("Failed to get machine type");
return NULL;
}
/* seccomp trans arch */
static char *seccomp_trans_arch(const char *arch)
{
lcr_arch_value arch_type[] = {
{ "SCMP_ARCH_X86", "[x86]" },
{ "SCMP_ARCH_X86_64", "[x86_64]" },
{ "SCMP_ARCH_X32", "[x32]" },
{ "SCMP_ARCH_ARM", "[arm]" },
{ "SCMP_ARCH_AARCH64", "[arm64]" },
{ "SCMP_ARCH_MIPS", "[mips]" },
{ "SCMP_ARCH_MIPS64", "[mips64]" },
{ "SCMP_ARCH_MIPS64N32", "[mips64n32]" },
{ "SCMP_ARCH_MIPSEL", "[mipsel]" },
{ "SCMP_ARCH_MIPSEL64", "[mipsel64]" },
{ "SCMP_ARCH_MIPSEL64N32", "[mipsel64n32]" },
{ "SCMP_ARCH_PPC", "[ppc]" },
{ "SCMP_ARCH_PPC64", "[ppc64]" },
{ "SCMP_ARCH_PPC64LE", "[ppc64le]" },
{ "SCMP_ARCH_S390", "[s390]" },
{ "SCMP_ARCH_S390X", "[s390x]" },
{ "SCMP_ARCH_PARISC", "[parisc]" },
{ "SCMP_ARCH_PARISC64", "[parisc64]" },
{ "SCMP_ARCH_ALL", "[all]" },
{ "SCMP_ARCH_AUTO", "" },
};
size_t len;
size_t i = 0;
len = sizeof(arch_type) / sizeof(lcr_arch_value);
for (i = 0; i < len; i++) {
if (strcmp(arch_type[i].arch, "SCMP_ARCH_AUTO") == 0) {
return get_hostarch();
} else if (strcmp(arch, arch_type[i].arch) == 0) {
return util_strdup_s(arch_type[i].value);
}
}
return NULL;
}
/* seccomp append arch */
static int seccomp_append_arch(char *arch, Buffer *buffer)
{
int ret = 0;
char *trans_arch = NULL;
if (arch == NULL) {
return -1;
}
trans_arch = seccomp_trans_arch(arch);
if (trans_arch == NULL) {
ERROR("Failed to translate seccomp arch: %s", arch);
return -1;
}
if (buffer_nappendf(buffer, strlen(trans_arch) + 2, "%s\n", trans_arch)) {
ERROR("Failed to append seccomp config head info\n");
ret = -1;
}
free(trans_arch);
return ret;
}
/* seccomp append rule */
static int seccomp_append_rule(const oci_runtime_defs_linux_syscall *syscall, size_t i, Buffer *buffer, char *action)
{
int ret = 0;
size_t j = 0;
if (syscall->names[i] == NULL) {
ERROR("Failed to get syscall name");
ret = -1;
goto out;
}
if (buffer_nappendf(buffer, strlen(syscall->names[i]) + strlen(action) + 2, "%s %s", syscall->names[i], action)) {
ERROR("Failed to append syscall name and action\n");
ret = -1;
goto out;
}
for (j = 0; j < syscall->args_len; j++) {
if ((syscall->args[j] == NULL) || (syscall->args[j]->op == NULL)) {
ERROR("Failed to get syscall args");
ret = -1;
goto out;
}
if (buffer_nappendf(buffer, 20 * 3 + strlen(syscall->args[j]->op), " [%u,%llu,%s,%llu]",
syscall->args[j]->index, syscall->args[j]->value, syscall->args[j]->op,
syscall->args[j]->value_two)) {
ERROR("Failed to append syscall rules\n");
ret = -1;
goto out;
}
}
if (buffer_nappendf(buffer, 2, "\n")) {
ERROR("Failed to append newline\n");
ret = -1;
goto out;
}
out:
return ret;
}
/* seccomp append rules */
static int seccomp_append_rules(const oci_runtime_defs_linux_syscall *syscall, Buffer *buffer)
{
int ret = 0;
size_t i = 0;
char *action = NULL;
if (syscall == NULL) {
return -1;
}
if ((syscall->action == NULL) || syscall->names_len == 0) {
return -1;
}
action = seccomp_trans_action(syscall->action);
if (action == NULL) {
ERROR("Failed to translate action");
ret = -1;
goto out;
}
for (i = 0; i < syscall->names_len; i++) {
if (seccomp_append_rule(syscall, i, buffer, action)) {
ret = -1;
goto out;
}
}
out:
free(action);
return ret;
}
static struct lcr_list *trans_oci_linux_sysctl(const json_map_string_string *sysctl)
{
struct lcr_list *conf = NULL;
struct lcr_list *node = NULL;
size_t i;
conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
for (i = 0; i < sysctl->len; i++) {
char sysk[BUFSIZ] = { 0 };
int nret = snprintf(sysk, sizeof(sysk), "lxc.sysctl.%s", sysctl->keys[i]);
if (nret < 0 || (size_t)nret >= sizeof(sysk)) {
ERROR("Failed to print string");
goto out_free;
}
node = create_lcr_list_node(sysk, sysctl->values[i]);
if (node == NULL) {
goto out_free;
}
lcr_list_add_tail(conf, node);
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
static int append_seccomp_with_archs(const oci_runtime_config_linux_seccomp *seccomp, Buffer *buffer)
{
int ret = 0;
size_t i = 0;
size_t j = 0;
for (i = 0; i < seccomp->architectures_len; i++) {
if (seccomp_append_arch(seccomp->architectures[i], buffer)) {
ret = -1;
goto out;
}
/* append rules */
for (j = 0; j < seccomp->syscalls_len; j++) {
if (seccomp_append_rules(seccomp->syscalls[j], buffer)) {
ret = -1;
goto out;
}
}
}
out:
return ret;
}
/* lxc seccomp conf format:
To support limit syscall arguments, extend the version 2 file to the following format:
syscall_name action [index,value,op,valueTwo] [index,value,op]...
for one arguments, [index,value,valueTwo,op]
index: the index for syscall arguments (type uint)
value: the value for syscall arguments (type uint64)
op: the operator for syscall arguments(string), a valid list of constants as of libseccomp v2.3.2 is
SCMP_CMP_NE,SCMP_CMP_LE,SCMP_CMP_LE, SCMP_CMP_EQ, SCMP_CMP_GE,
SCMP_CMP_GT, SCMP_CMP_MASKED_EQ, or !=,<=,==,>=,>,&=
valueTwo: the value for syscall arguments only used for mask eq (type uint64, optional)
For example:
2
blacklist allow
reject_force_umount # comment this to allow umount -f; not recommended
[all]
kexec_load errno 1 [0,1,SCMP_CMP_LE][3,1,==][5,1,SCMP_CMP_MASKED_EQ,1]
open_by_handle_at errno 1
init_module errno 1
finit_module errno 1
delete_module errno 1
*/
static int trans_oci_seccomp(const oci_runtime_config_linux_seccomp *seccomp, char **seccomp_conf)
{
int ret = 0;
size_t j = 0;
size_t init_size = 4 * SIZE_KB;
Buffer *buffer = buffer_alloc(init_size);
if (buffer == NULL) {
ERROR("Failed to malloc output_buffer\n");
return -1;
}
/* config version */
if (buffer_nappendf(buffer, 3, "2\n")) {
ERROR("Failed to append seccomp config version\n");
ret = -1;
goto out_free;
}
/* append head info */
if (seccomp_append_head_info(seccomp->default_action, buffer)) {
ret = -1;
goto out_free;
}
/* append architectures */
if (seccomp->architectures_len != 0) {
ret = append_seccomp_with_archs(seccomp, buffer);
if (ret != 0) {
goto out_free;
}
} else {
// add rules directly(eg: blacklist)
for (j = 0; j < seccomp->syscalls_len; j++) {
if (seccomp_append_rules(seccomp->syscalls[j], buffer)) {
ret = -1;
goto out_free;
}
}
}
*seccomp_conf = buffer_to_s(buffer);
if (*seccomp_conf == NULL) {
ret = -1;
goto out_free;
}
out_free:
buffer_free(buffer);
return ret;
}
/* trans oci linux */
struct lcr_list *trans_oci_linux(const oci_runtime_config_linux *l, char **seccomp_conf)
{
int ret = 0;
struct lcr_list *tmp = NULL;
struct lcr_list *conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
// UID/GID Mapping
tmp = trans_oci_id_mapping(l);
if (tmp == NULL) {
goto out_free;
}
lcr_list_merge(conf, tmp);
// Resources
if (l->resources != NULL) {
tmp = trans_oci_resources(l->resources);
if (tmp == NULL) {
goto out_free;
}
lcr_list_merge(conf, tmp);
}
// linux devices
tmp = trans_oci_linux_devices(l);
if (tmp == NULL) {
goto out_free;
}
lcr_list_merge(conf, tmp);
// Namespaces
tmp = trans_oci_namespaces(l);
if (tmp == NULL) {
goto out_free;
}
lcr_list_merge(conf, tmp);
// MaskedPaths and ReadonlyPaths
tmp = trans_oci_mask_ro_paths(l);
if (tmp == NULL) {
goto out_free;
}
lcr_list_merge(conf, tmp);
// sysctl
if (l->sysctl != NULL && l->uid_mappings == NULL && l->gid_mappings == NULL) {
tmp = trans_oci_linux_sysctl(l->sysctl);
if (tmp == NULL) {
goto out_free;
}
lcr_list_merge(conf, tmp);
}
// seccomp
if (l->seccomp != NULL && seccomp_conf != NULL) {
ret = trans_oci_seccomp(l->seccomp, seccomp_conf);
if (ret) {
goto out_free;
}
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
/* trans annotations */
struct lcr_list *trans_annotations(const json_map_string_string *anno)
{
size_t i, j;
size_t len;
int ret = 0;
struct lcr_list *conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
len = sizeof(g_require_annotations) / sizeof(lcr_annotation_item_t);
// trans annotations
for (i = 0; i < anno->len; i++) {
if (anno->keys[i] == NULL) {
continue;
}
for (j = 0; j < len; j++) {
if (strcmp(anno->keys[i], g_require_annotations[j].name) != 0) {
continue;
}
ret = g_require_annotations[j].checker(anno->values[i]);
if (ret == -1) {
ERROR("item: %s, value: %s, checker failed", anno->keys[i], anno->values[i]);
goto out_free;
} else if (ret == 1) {
DEBUG("Skip this config item: %s", anno->keys[i]);
continue;
}
struct lcr_list *node = create_lcr_list_node(g_require_annotations[j].lxc_item_name, anno->values[i]);
if (node == NULL) {
goto out_free;
}
lcr_list_add_tail(conf, node);
break;
}
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}
static int add_needed_pty_conf(struct lcr_list *conf)
{
struct lcr_list *node = create_lcr_list_node("lxc.pty.max", "1024");
if (node == NULL) {
return -1;
}
lcr_list_add_tail(conf, node);
return 0;
}
static int add_needed_net_conf(struct lcr_list *conf)
{
struct lcr_list *node = create_lcr_list_node("lxc.net.0.type", "empty");
if (node == NULL) {
return -1;
}
lcr_list_add_tail(conf, node);
node = create_lcr_list_node("lxc.net.0.flags", "up");
if (node == NULL) {
return -1;
}
lcr_list_add_tail(conf, node);
return 0;
}
/* get needed lxc conf */
struct lcr_list *get_needed_lxc_conf()
{
struct lcr_list *conf = util_common_calloc_s(sizeof(*conf));
if (conf == NULL) {
return NULL;
}
lcr_list_init(conf);
if (add_needed_pty_conf(conf) < 0) {
goto out_free;
}
if (add_needed_net_conf(conf) < 0) {
goto out_free;
}
return conf;
out_free:
lcr_free_config(conf);
free(conf);
return NULL;
}