2591 lines
66 KiB
C
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;
|
|
}
|