diff --git a/utils/lxc/Makefile b/utils/lxc/Makefile index 55ae80b335..3865df8f1d 100644 --- a/utils/lxc/Makefile +++ b/utils/lxc/Makefile @@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=lxc PKG_VERSION:=5.0.3 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://linuxcontainers.org/downloads/lxc/ diff --git a/utils/lxc/patches/001-confile-move-lxc_fill_elevated_privileges-to-tools-l.patch b/utils/lxc/patches/001-confile-move-lxc_fill_elevated_privileges-to-tools-l.patch new file mode 100644 index 0000000000..ada94274a1 --- /dev/null +++ b/utils/lxc/patches/001-confile-move-lxc_fill_elevated_privileges-to-tools-l.patch @@ -0,0 +1,144 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sat, 17 Feb 2024 16:43:21 +0100 +Subject: [PATCH] confile: move lxc_fill_elevated_privileges() to + tools/lxc_attach + +lxc_fill_elevated_privileges() is used only in lxc-attach tool, +let's move this function in there. + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit 672b2172de2277e950e05d2abe0b1115fa6c3f53) +--- + src/lxc/confile.c | 45 ------------------------------------- + src/lxc/confile.h | 2 -- + src/lxc/tools/lxc_attach.c | 46 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 46 insertions(+), 47 deletions(-) + +--- a/src/lxc/confile.c ++++ b/src/lxc/confile.c +@@ -3295,51 +3295,6 @@ int lxc_config_parse_arch(const char *ar + return ret_errno(EINVAL); + } + +-int lxc_fill_elevated_privileges(char *flaglist, unsigned int *flags) +-{ +- unsigned int flags_tmp = 0; +- char *token; +- struct { +- const char *token; +- int flag; +- } all_privs[] = { +- { "CGROUP", LXC_ATTACH_MOVE_TO_CGROUP }, +- { "CAP", LXC_ATTACH_DROP_CAPABILITIES }, +- { "LSM", LXC_ATTACH_LSM_EXEC }, +- { NULL, 0 } +- }; +- +- if (!flaglist) { +- /* +- * For the sake of backward compatibility, keep all privileges +- * if no specific privileges are specified. +- */ +- for (unsigned int i = 0; all_privs[i].token; i++) +- flags_tmp |= all_privs[i].flag; +- +- *flags = flags_tmp; +- return 0; +- } +- +- lxc_iterate_parts(token, flaglist, "|") { +- bool valid_token = false; +- +- for (unsigned int i = 0; all_privs[i].token; i++) { +- if (!strequal(all_privs[i].token, token)) +- continue; +- +- valid_token = true; +- flags_tmp |= all_privs[i].flag; +- } +- +- if (!valid_token) +- return syserror_set(-EINVAL, "Invalid elevated privilege \"%s\" requested", token); +- } +- +- *flags = flags_tmp; +- return 0; +-} +- + /* Write out a configuration file. */ + int write_config(int fd, const struct lxc_conf *conf) + { +--- a/src/lxc/confile.h ++++ b/src/lxc/confile.h +@@ -90,8 +90,6 @@ __hidden extern void lxc_config_define_f + */ + __hidden extern int lxc_config_parse_arch(const char *arch, signed long *persona); + +-__hidden extern int lxc_fill_elevated_privileges(char *flaglist, unsigned int *flags); +- + __hidden extern int lxc_clear_config_item(struct lxc_conf *c, const char *key); + + __hidden extern int write_config(int fd, const struct lxc_conf *conf); +--- a/src/lxc/tools/lxc_attach.c ++++ b/src/lxc/tools/lxc_attach.c +@@ -46,6 +46,7 @@ __attribute__((constructor)) static void + #endif + + static int my_parser(struct lxc_arguments *args, int c, char *arg); ++static int lxc_fill_elevated_privileges(char *flaglist, unsigned int *flags); + static int add_to_simple_array(char ***array, ssize_t *capacity, char *value); + static bool stdfd_is_pty(void); + static int lxc_attach_create_log_file(const char *log_file); +@@ -213,6 +214,51 @@ static int my_parser(struct lxc_argument + return 0; + } + ++static int lxc_fill_elevated_privileges(char *flaglist, unsigned int *flags) ++{ ++ unsigned int flags_tmp = 0; ++ char *token; ++ struct { ++ const char *token; ++ int flag; ++ } all_privs[] = { ++ { "CGROUP", LXC_ATTACH_MOVE_TO_CGROUP }, ++ { "CAP", LXC_ATTACH_DROP_CAPABILITIES }, ++ { "LSM", LXC_ATTACH_LSM_EXEC }, ++ { NULL, 0 } ++ }; ++ ++ if (!flaglist) { ++ /* ++ * For the sake of backward compatibility, keep all privileges ++ * if no specific privileges are specified. ++ */ ++ for (unsigned int i = 0; all_privs[i].token; i++) ++ flags_tmp |= all_privs[i].flag; ++ ++ *flags = flags_tmp; ++ return 0; ++ } ++ ++ lxc_iterate_parts(token, flaglist, "|") { ++ bool valid_token = false; ++ ++ for (unsigned int i = 0; all_privs[i].token; i++) { ++ if (!strequal(all_privs[i].token, token)) ++ continue; ++ ++ valid_token = true; ++ flags_tmp |= all_privs[i].flag; ++ } ++ ++ if (!valid_token) ++ return syserror_set(-EINVAL, "Invalid elevated privilege \"%s\" requested", token); ++ } ++ ++ *flags = flags_tmp; ++ return 0; ++} ++ + static int add_to_simple_array(char ***array, ssize_t *capacity, char *value) + { + ssize_t count = 0; diff --git a/utils/lxc/patches/002-meson-introduce-IN_LIBLXC-preprocessor-macro.patch b/utils/lxc/patches/002-meson-introduce-IN_LIBLXC-preprocessor-macro.patch new file mode 100644 index 0000000000..9f2e886d9a --- /dev/null +++ b/utils/lxc/patches/002-meson-introduce-IN_LIBLXC-preprocessor-macro.patch @@ -0,0 +1,78 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sat, 17 Feb 2024 16:47:41 +0100 +Subject: [PATCH] meson: introduce IN_LIBLXC preprocessor macro + +The purpose of it is to tell us if we are compiling +liblxc or lxc test/tool/command. + +This thing is needed to exclude unnecessary functions +from being compiled-in in the resulting executables +like lxc-start, lxc-attach, etc. + +The problem is that lxc tools (lxc-start, lxc-stop, etc) +depend not only on the liblxc as a shared library, but also +require some non-exported symbols or helpers from liblxc +internals. So, we have to link these executables with some liblxc +object files directly which results in the dependency hell, +because linking one .c file from liblxc may end up having to +link with another one (what contains some dependency) and so on. +By using IN_LIBLXC in the liblxc internals we can selectively +omit some functions from being compiled in such cases. + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit f14656ebf62ab0dbb836431e2781cfd363f4e4aa) +--- + src/lxc/meson.build | 2 +- + src/lxc/state.c | 4 ++++ + src/lxc/utils.c | 4 ++++ + 3 files changed, 9 insertions(+), 1 deletion(-) + +--- a/src/lxc/meson.build ++++ b/src/lxc/meson.build +@@ -157,7 +157,7 @@ liblxc_static = static_library( + install: true, + include_directories: liblxc_includes, + dependencies: [threads] + liblxc_dependency_headers, +- c_args: '-fvisibility=default') ++ c_args: ['-fvisibility=default', '-DIN_LIBLXC']) + + lxc_functions = configure_file( + configuration: conf, +--- a/src/lxc/state.c ++++ b/src/lxc/state.c +@@ -52,6 +52,8 @@ lxc_state_t lxc_str2state(const char *st + return -1; + } + ++#ifdef IN_LIBLXC ++ + lxc_state_t lxc_getstate(const char *name, const char *lxcpath) + { + return lxc_cmd_get_state(name, lxcpath); +@@ -117,3 +119,5 @@ int lxc_wait(const char *lxcname, const + + return 0; + } ++ ++#endif /* IN_LIBLXC */ +--- a/src/lxc/utils.c ++++ b/src/lxc/utils.c +@@ -61,6 +61,8 @@ lxc_log_define(utils, lxc); + */ + extern bool btrfs_try_remove_subvol(const char *path); + ++#ifdef IN_LIBLXC ++ + static int _recursive_rmdir(const char *dirname, dev_t pdev, + const char *exclude, int level, bool onedev) + { +@@ -193,6 +195,8 @@ extern int lxc_rmdir_onedev(const char * + return _recursive_rmdir(path, mystat.st_dev, exclude, 0, onedev); + } + ++#endif /* IN_LIBLXC */ ++ + /* borrowed from iproute2 */ + extern int get_u16(unsigned short *val, const char *arg, int base) + { diff --git a/utils/lxc/patches/003-confile-unhide-lxc_config_define-helpers.patch b/utils/lxc/patches/003-confile-unhide-lxc_config_define-helpers.patch new file mode 100644 index 0000000000..186d886e42 --- /dev/null +++ b/utils/lxc/patches/003-confile-unhide-lxc_config_define-helpers.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sat, 17 Feb 2024 16:58:02 +0100 +Subject: [PATCH] confile: unhide lxc_config_define*() helpers + +Let's unhide lxc_config_define_add, lxc_config_define_load and +lxc_config_define_free helpers. These functions are safe enough +to be used by external tools. Semantic is also clear. + +Reason is that we have lxc-start/lxc-execute tools which +use these symbols. Right now it works, because we just +link a whole liblxc statically to each lxc-* tool... + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit b7591ad49d6051047371824bae6f19e37ba86dea) +--- + src/lxc/confile.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/src/lxc/confile.h ++++ b/src/lxc/confile.h +@@ -77,11 +77,11 @@ __hidden extern int lxc_config_read(cons + + __hidden extern int append_unexp_config_line(const char *line, struct lxc_conf *conf); + +-__hidden extern int lxc_config_define_add(struct lxc_list *defines, char *arg); ++extern int lxc_config_define_add(struct lxc_list *defines, char *arg); + +-__hidden extern bool lxc_config_define_load(struct lxc_list *defines, struct lxc_container *c); ++extern bool lxc_config_define_load(struct lxc_list *defines, struct lxc_container *c); + +-__hidden extern void lxc_config_define_free(struct lxc_list *defines); ++extern void lxc_config_define_free(struct lxc_list *defines); + + #define LXC_ARCH_UNCHANGED 0xffffffffL + /* diff --git a/utils/lxc/patches/004-conf-reorganize-split-code-to-idmap_utils.c.patch b/utils/lxc/patches/004-conf-reorganize-split-code-to-idmap_utils.c.patch new file mode 100644 index 0000000000..20eecc6afd --- /dev/null +++ b/utils/lxc/patches/004-conf-reorganize-split-code-to-idmap_utils.c.patch @@ -0,0 +1,939 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sun, 18 Feb 2024 15:17:25 +0100 +Subject: [PATCH] conf: reorganize/split code to idmap_utils.c + +Move some idmaps-related functions from lxc/conf.c +to a new idmap_utils.c file. + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit 863c59dc3aca086a892ad44eb2dfa53438544944) +--- + src/lxc/cmd/lxc_usernsexec.c | 1 + + src/lxc/conf.c | 371 +-------------------------------- + src/lxc/conf.h | 6 - + src/lxc/idmap_utils.c | 391 +++++++++++++++++++++++++++++++++++ + src/lxc/idmap_utils.h | 30 +++ + src/lxc/lxccontainer.c | 1 + + src/lxc/meson.build | 2 + + src/lxc/start.c | 1 + + src/lxc/tools/lxc_unshare.c | 1 + + 9 files changed, 428 insertions(+), 376 deletions(-) + create mode 100644 src/lxc/idmap_utils.c + create mode 100644 src/lxc/idmap_utils.h + +--- a/src/lxc/cmd/lxc_usernsexec.c ++++ b/src/lxc/cmd/lxc_usernsexec.c +@@ -22,6 +22,7 @@ + #include "compiler.h" + #include "conf.h" + #include "hlist.h" ++#include "idmap_utils.h" + #include "list.h" + #include "log.h" + #include "macro.h" +--- a/src/lxc/conf.c ++++ b/src/lxc/conf.c +@@ -41,6 +41,7 @@ + #include "confile.h" + #include "confile_utils.h" + #include "error.h" ++#include "idmap_utils.h" + #include "log.h" + #include "lsm/lsm.h" + #include "lxclock.h" +@@ -1664,35 +1665,6 @@ static int lxc_setup_rootfs_switch_root( + return lxc_pivot_root(rootfs); + } + +-static const struct id_map *find_mapped_nsid_entry(const struct lxc_conf *conf, +- unsigned id, +- enum idtype idtype) +-{ +- struct id_map *map; +- struct id_map *retmap = NULL; +- +- /* Shortcut for container's root mappings. */ +- if (id == 0) { +- if (idtype == ID_TYPE_UID) +- return conf->root_nsuid_map; +- +- if (idtype == ID_TYPE_GID) +- return conf->root_nsgid_map; +- } +- +- list_for_each_entry(map, &conf->id_map, head) { +- if (map->idtype != idtype) +- continue; +- +- if (id >= map->nsid && id < map->nsid + map->range) { +- retmap = map; +- break; +- } +- } +- +- return retmap; +-} +- + static int lxc_recv_devpts_from_child(struct lxc_handler *handler) + { + int ret; +@@ -3476,243 +3448,6 @@ struct lxc_conf *lxc_conf_init(void) + return new; + } + +-int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, +- size_t buf_size) +-{ +- __do_close int fd = -EBADF; +- int ret; +- char path[PATH_MAX]; +- +- if (geteuid() != 0 && idtype == ID_TYPE_GID) { +- __do_close int setgroups_fd = -EBADF; +- +- ret = strnprintf(path, sizeof(path), "/proc/%d/setgroups", pid); +- if (ret < 0) +- return -E2BIG; +- +- setgroups_fd = open(path, O_WRONLY); +- if (setgroups_fd < 0 && errno != ENOENT) +- return log_error_errno(-1, errno, "Failed to open \"%s\"", path); +- +- if (setgroups_fd >= 0) { +- ret = lxc_write_nointr(setgroups_fd, "deny\n", +- STRLITERALLEN("deny\n")); +- if (ret != STRLITERALLEN("deny\n")) +- return log_error_errno(-1, errno, "Failed to write \"deny\" to \"/proc/%d/setgroups\"", pid); +- TRACE("Wrote \"deny\" to \"/proc/%d/setgroups\"", pid); +- } +- } +- +- ret = strnprintf(path, sizeof(path), "/proc/%d/%cid_map", pid, +- idtype == ID_TYPE_UID ? 'u' : 'g'); +- if (ret < 0) +- return -E2BIG; +- +- fd = open(path, O_WRONLY | O_CLOEXEC); +- if (fd < 0) +- return log_error_errno(-1, errno, "Failed to open \"%s\"", path); +- +- ret = lxc_write_nointr(fd, buf, buf_size); +- if (ret < 0 || (size_t)ret != buf_size) +- return log_error_errno(-1, errno, "Failed to write %cid mapping to \"%s\"", +- idtype == ID_TYPE_UID ? 'u' : 'g', path); +- +- return 0; +-} +- +-/* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both. +- * +- * @return 1 if functional binary was found +- * @return 0 if binary exists but is lacking privilege +- * @return -ENOENT if binary does not exist +- * @return -EINVAL if cap to check is neither CAP_SETUID nor CAP_SETGID +- */ +-static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap) +-{ +- __do_free char *path = NULL; +- int ret; +- struct stat st; +- +- if (cap != CAP_SETUID && cap != CAP_SETGID) +- return ret_errno(EINVAL); +- +- path = on_path(binary, NULL); +- if (!path) +- return ret_errno(ENOENT); +- +- ret = stat(path, &st); +- if (ret < 0) +- return -errno; +- +- /* Check if the binary is setuid. */ +- if (st.st_mode & S_ISUID) +- return log_debug(1, "The binary \"%s\" does have the setuid bit set", path); +- +-#if HAVE_LIBCAP && LIBCAP_SUPPORTS_FILE_CAPABILITIES +- /* Check if it has the CAP_SETUID capability. */ +- if ((cap & CAP_SETUID) && +- lxc_file_cap_is_set(path, CAP_SETUID, CAP_EFFECTIVE) && +- lxc_file_cap_is_set(path, CAP_SETUID, CAP_PERMITTED)) +- return log_debug(1, "The binary \"%s\" has CAP_SETUID in its CAP_EFFECTIVE and CAP_PERMITTED sets", path); +- +- /* Check if it has the CAP_SETGID capability. */ +- if ((cap & CAP_SETGID) && +- lxc_file_cap_is_set(path, CAP_SETGID, CAP_EFFECTIVE) && +- lxc_file_cap_is_set(path, CAP_SETGID, CAP_PERMITTED)) +- return log_debug(1, "The binary \"%s\" has CAP_SETGID in its CAP_EFFECTIVE and CAP_PERMITTED sets", path); +- +- return 0; +-#else +- /* +- * If we cannot check for file capabilities we need to give the benefit +- * of the doubt. Otherwise we might fail even though all the necessary +- * file capabilities are set. +- */ +- DEBUG("Cannot check for file capabilities as full capability support is missing. Manual intervention needed"); +- return 1; +-#endif +-} +- +-static int lxc_map_ids_exec_wrapper(void *args) +-{ +- execl("/bin/sh", "sh", "-c", (char *)args, (char *)NULL); +- return -1; +-} +- +-static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap, +- unsigned id, enum idtype idtype); +- +-int lxc_map_ids(struct list_head *idmap, pid_t pid) +-{ +- int fill, left; +- uid_t hostuid; +- gid_t hostgid; +- char u_or_g; +- char *pos; +- char cmd_output[PATH_MAX]; +- struct id_map *map; +- enum idtype type; +- int ret = 0, gidmap = 0, uidmap = 0; +- char mapbuf[STRLITERALLEN("new@idmap") + STRLITERALLEN(" ") + +- INTTYPE_TO_STRLEN(pid_t) + STRLITERALLEN(" ") + +- LXC_IDMAPLEN] = {0}; +- bool had_entry = false, maps_host_root = false, use_shadow = false; +- +- hostuid = geteuid(); +- hostgid = getegid(); +- +- /* +- * Check whether caller wants to map host root. +- * Due to a security fix newer kernels require CAP_SETFCAP when mapping +- * host root into the child userns as you would be able to write fscaps +- * that would be valid in the ancestor userns. Mapping host root should +- * rarely be the case but LXC is being clever in a bunch of cases. +- */ +- if (find_mapped_hostid_entry(idmap, 0, ID_TYPE_UID)) +- maps_host_root = true; +- +- /* If new{g,u}idmap exists, that is, if shadow is handing out subuid +- * ranges, then insist that root also reserve ranges in subuid. This +- * will protected it by preventing another user from being handed the +- * range by shadow. +- */ +- uidmap = idmaptool_on_path_and_privileged("newuidmap", CAP_SETUID); +- if (uidmap == -ENOENT) +- WARN("newuidmap binary is missing"); +- else if (!uidmap) +- WARN("newuidmap is lacking necessary privileges"); +- +- gidmap = idmaptool_on_path_and_privileged("newgidmap", CAP_SETGID); +- if (gidmap == -ENOENT) +- WARN("newgidmap binary is missing"); +- else if (!gidmap) +- WARN("newgidmap is lacking necessary privileges"); +- +- if (maps_host_root) { +- INFO("Caller maps host root. Writing mapping directly"); +- } else if (uidmap > 0 && gidmap > 0) { +- DEBUG("Functional newuidmap and newgidmap binary found"); +- use_shadow = true; +- } else { +- /* In case unprivileged users run application containers via +- * execute() or a start*() there are valid cases where they may +- * only want to map their own {g,u}id. Let's not block them from +- * doing so by requiring geteuid() == 0. +- */ +- DEBUG("No newuidmap and newgidmap binary found. Trying to " +- "write directly with euid %d", hostuid); +- } +- +- /* Check if we really need to use newuidmap and newgidmap. +- * If the user is only remapping their own {g,u}id, we don't need it. +- */ +- if (use_shadow && list_len(map, idmap, head) == 2) { +- use_shadow = false; +- list_for_each_entry(map, idmap, head) { +- if (map->idtype == ID_TYPE_UID && map->range == 1 && +- map->nsid == hostuid && map->hostid == hostuid) +- continue; +- if (map->idtype == ID_TYPE_GID && map->range == 1 && +- map->nsid == hostgid && map->hostid == hostgid) +- continue; +- use_shadow = true; +- break; +- } +- } +- +- for (type = ID_TYPE_UID, u_or_g = 'u'; type <= ID_TYPE_GID; +- type++, u_or_g = 'g') { +- pos = mapbuf; +- +- if (use_shadow) +- pos += sprintf(mapbuf, "new%cidmap %d", u_or_g, pid); +- +- list_for_each_entry(map, idmap, head) { +- if (map->idtype != type) +- continue; +- +- had_entry = true; +- +- left = LXC_IDMAPLEN - (pos - mapbuf); +- fill = strnprintf(pos, left, "%s%lu %lu %lu%s", +- use_shadow ? " " : "", map->nsid, +- map->hostid, map->range, +- use_shadow ? "" : "\n"); +- /* +- * The kernel only takes <= 4k for writes to +- * /proc//{g,u}id_map +- */ +- if (fill <= 0) +- return log_error_errno(-1, errno, "Too many %cid mappings defined", u_or_g); +- +- pos += fill; +- } +- if (!had_entry) +- continue; +- +- /* Try to catch the output of new{g,u}idmap to make debugging +- * easier. +- */ +- if (use_shadow) { +- ret = run_command(cmd_output, sizeof(cmd_output), +- lxc_map_ids_exec_wrapper, +- (void *)mapbuf); +- if (ret < 0) +- return log_error(-1, "new%cidmap failed to write mapping \"%s\": %s", u_or_g, cmd_output, mapbuf); +- TRACE("new%cidmap wrote mapping \"%s\"", u_or_g, mapbuf); +- } else { +- ret = write_id_mapping(type, pid, mapbuf, pos - mapbuf); +- if (ret < 0) +- return log_error(-1, "Failed to write mapping: %s", mapbuf); +- TRACE("Wrote mapping \"%s\"", mapbuf); +- } +- +- memset(mapbuf, 0, sizeof(mapbuf)); +- } +- +- return 0; +-} +- + /* + * Return the host uid/gid to which the container root is mapped in val. + * Return true if id was found, false otherwise. +@@ -3741,40 +3476,6 @@ static id_t get_mapped_rootid(const stru + return LXC_INVALID_GID; + } + +-int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype) +-{ +- struct id_map *map; +- +- list_for_each_entry(map, &conf->id_map, head) { +- if (map->idtype != idtype) +- continue; +- +- if (id >= map->hostid && id < map->hostid + map->range) +- return (id - map->hostid) + map->nsid; +- } +- +- return -1; +-} +- +-int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype) +-{ +- struct id_map *map; +- unsigned int freeid = 0; +- +-again: +- list_for_each_entry(map, &conf->id_map, head) { +- if (map->idtype != idtype) +- continue; +- +- if (freeid >= map->nsid && freeid < map->nsid + map->range) { +- freeid = map->nsid + map->range; +- goto again; +- } +- } +- +- return freeid; +-} +- + /* + * Mount a proc under @rootfs if proc self points to a pid other than + * my own. This is needed to have a known-good proc mount for setting +@@ -4910,76 +4611,6 @@ static int run_userns_fn(void *data) + return d->fn(d->arg); + } + +-static struct id_map *mapped_nsid_add(const struct lxc_conf *conf, unsigned id, +- enum idtype idtype) +-{ +- const struct id_map *map; +- struct id_map *retmap; +- +- map = find_mapped_nsid_entry(conf, id, idtype); +- if (!map) +- return NULL; +- +- retmap = zalloc(sizeof(*retmap)); +- if (!retmap) +- return NULL; +- +- memcpy(retmap, map, sizeof(*retmap)); +- return retmap; +-} +- +-static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap, +- unsigned id, enum idtype idtype) +-{ +- struct id_map *retmap = NULL; +- struct id_map *map; +- +- list_for_each_entry(map, idmap, head) { +- if (map->idtype != idtype) +- continue; +- +- if (id >= map->hostid && id < map->hostid + map->range) { +- retmap = map; +- break; +- } +- } +- +- return retmap; +-} +- +-/* Allocate a new {g,u}id mapping for the given {g,u}id. Re-use an already +- * existing one or establish a new one. +- */ +-static struct id_map *mapped_hostid_add(const struct lxc_conf *conf, uid_t id, +- enum idtype type) +-{ +- __do_free struct id_map *entry = NULL; +- int hostid_mapped; +- struct id_map *tmp = NULL; +- +- entry = zalloc(sizeof(*entry)); +- if (!entry) +- return NULL; +- +- /* Reuse existing mapping. */ +- tmp = find_mapped_hostid_entry(&conf->id_map, id, type); +- if (tmp) { +- memcpy(entry, tmp, sizeof(*entry)); +- } else { +- /* Find new mapping. */ +- hostid_mapped = find_unmapped_nsid(conf, type); +- if (hostid_mapped < 0) +- return log_debug(NULL, "Failed to find free mapping for id %d", id); +- +- entry->idtype = type; +- entry->nsid = hostid_mapped; +- entry->hostid = (unsigned long)id; +- entry->range = 1; +- } +- +- return move_ptr(entry); +-} +- + static int get_minimal_idmap(const struct lxc_conf *conf, uid_t *resuid, + gid_t *resgid, struct list_head *head_ret) + { +--- a/src/lxc/conf.h ++++ b/src/lxc/conf.h +@@ -578,9 +578,6 @@ struct lxc_conf { + __u64 sched_core_cookie; + }; + +-__hidden extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size) +- __access_r(3, 4); +- + extern thread_local struct lxc_conf *current_config; + + __hidden extern int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, char *argv[]); +@@ -592,7 +589,6 @@ __hidden extern void lxc_storage_put(str + __hidden extern int lxc_rootfs_init(struct lxc_conf *conf, bool userns); + __hidden extern int lxc_rootfs_prepare_parent(struct lxc_handler *handler); + __hidden extern int lxc_idmapped_mounts_parent(struct lxc_handler *handler); +-__hidden extern int lxc_map_ids(struct list_head *idmap, pid_t pid); + __hidden extern int lxc_create_tty(const char *name, struct lxc_conf *conf); + __hidden extern void lxc_delete_tty(struct lxc_tty_info *ttys); + __hidden extern int lxc_clear_config_caps(struct lxc_conf *c); +@@ -611,8 +607,6 @@ __hidden extern int lxc_setup_rootfs_pre + __hidden extern int lxc_setup(struct lxc_handler *handler); + __hidden extern int lxc_setup_parent(struct lxc_handler *handler); + __hidden extern int setup_resource_limits(struct lxc_conf *conf, pid_t pid); +-__hidden extern int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype); +-__hidden extern int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype); + __hidden extern int userns_exec_1(const struct lxc_conf *conf, int (*fn)(void *), void *data, + const char *fn_name); + __hidden extern int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data, +--- /dev/null ++++ b/src/lxc/idmap_utils.c +@@ -0,0 +1,391 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "lxc.h" ++ ++#include "log.h" ++#include "lxclock.h" ++#include "memory_utils.h" ++#include "utils.h" ++ ++lxc_log_define(idmap_utils, lxc); ++ ++int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, ++ size_t buf_size) ++{ ++ __do_close int fd = -EBADF; ++ int ret; ++ char path[PATH_MAX]; ++ ++ if (geteuid() != 0 && idtype == ID_TYPE_GID) { ++ __do_close int setgroups_fd = -EBADF; ++ ++ ret = strnprintf(path, sizeof(path), "/proc/%d/setgroups", pid); ++ if (ret < 0) ++ return -E2BIG; ++ ++ setgroups_fd = open(path, O_WRONLY); ++ if (setgroups_fd < 0 && errno != ENOENT) ++ return log_error_errno(-1, errno, "Failed to open \"%s\"", path); ++ ++ if (setgroups_fd >= 0) { ++ ret = lxc_write_nointr(setgroups_fd, "deny\n", ++ STRLITERALLEN("deny\n")); ++ if (ret != STRLITERALLEN("deny\n")) ++ return log_error_errno(-1, errno, "Failed to write \"deny\" to \"/proc/%d/setgroups\"", pid); ++ TRACE("Wrote \"deny\" to \"/proc/%d/setgroups\"", pid); ++ } ++ } ++ ++ ret = strnprintf(path, sizeof(path), "/proc/%d/%cid_map", pid, ++ idtype == ID_TYPE_UID ? 'u' : 'g'); ++ if (ret < 0) ++ return -E2BIG; ++ ++ fd = open(path, O_WRONLY | O_CLOEXEC); ++ if (fd < 0) ++ return log_error_errno(-1, errno, "Failed to open \"%s\"", path); ++ ++ ret = lxc_write_nointr(fd, buf, buf_size); ++ if (ret < 0 || (size_t)ret != buf_size) ++ return log_error_errno(-1, errno, "Failed to write %cid mapping to \"%s\"", ++ idtype == ID_TYPE_UID ? 'u' : 'g', path); ++ ++ return 0; ++} ++ ++/* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both. ++ * ++ * @return 1 if functional binary was found ++ * @return 0 if binary exists but is lacking privilege ++ * @return -ENOENT if binary does not exist ++ * @return -EINVAL if cap to check is neither CAP_SETUID nor CAP_SETGID ++ */ ++static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap) ++{ ++ __do_free char *path = NULL; ++ int ret; ++ struct stat st; ++ ++ if (cap != CAP_SETUID && cap != CAP_SETGID) ++ return ret_errno(EINVAL); ++ ++ path = on_path(binary, NULL); ++ if (!path) ++ return ret_errno(ENOENT); ++ ++ ret = stat(path, &st); ++ if (ret < 0) ++ return -errno; ++ ++ /* Check if the binary is setuid. */ ++ if (st.st_mode & S_ISUID) ++ return log_debug(1, "The binary \"%s\" does have the setuid bit set", path); ++ ++#if HAVE_LIBCAP && LIBCAP_SUPPORTS_FILE_CAPABILITIES ++ /* Check if it has the CAP_SETUID capability. */ ++ if ((cap & CAP_SETUID) && ++ lxc_file_cap_is_set(path, CAP_SETUID, CAP_EFFECTIVE) && ++ lxc_file_cap_is_set(path, CAP_SETUID, CAP_PERMITTED)) ++ return log_debug(1, "The binary \"%s\" has CAP_SETUID in its CAP_EFFECTIVE and CAP_PERMITTED sets", path); ++ ++ /* Check if it has the CAP_SETGID capability. */ ++ if ((cap & CAP_SETGID) && ++ lxc_file_cap_is_set(path, CAP_SETGID, CAP_EFFECTIVE) && ++ lxc_file_cap_is_set(path, CAP_SETGID, CAP_PERMITTED)) ++ return log_debug(1, "The binary \"%s\" has CAP_SETGID in its CAP_EFFECTIVE and CAP_PERMITTED sets", path); ++ ++ return 0; ++#else ++ /* ++ * If we cannot check for file capabilities we need to give the benefit ++ * of the doubt. Otherwise we might fail even though all the necessary ++ * file capabilities are set. ++ */ ++ DEBUG("Cannot check for file capabilities as full capability support is missing. Manual intervention needed"); ++ return 1; ++#endif ++} ++ ++static int lxc_map_ids_exec_wrapper(void *args) ++{ ++ execl("/bin/sh", "sh", "-c", (char *)args, (char *)NULL); ++ return -1; ++} ++ ++static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap, ++ unsigned id, enum idtype idtype); ++ ++int lxc_map_ids(struct list_head *idmap, pid_t pid) ++{ ++ int fill, left; ++ uid_t hostuid; ++ gid_t hostgid; ++ char u_or_g; ++ char *pos; ++ char cmd_output[PATH_MAX]; ++ struct id_map *map; ++ enum idtype type; ++ int ret = 0, gidmap = 0, uidmap = 0; ++ char mapbuf[STRLITERALLEN("new@idmap") + STRLITERALLEN(" ") + ++ INTTYPE_TO_STRLEN(pid_t) + STRLITERALLEN(" ") + ++ LXC_IDMAPLEN] = {0}; ++ bool had_entry = false, maps_host_root = false, use_shadow = false; ++ ++ hostuid = geteuid(); ++ hostgid = getegid(); ++ ++ /* ++ * Check whether caller wants to map host root. ++ * Due to a security fix newer kernels require CAP_SETFCAP when mapping ++ * host root into the child userns as you would be able to write fscaps ++ * that would be valid in the ancestor userns. Mapping host root should ++ * rarely be the case but LXC is being clever in a bunch of cases. ++ */ ++ if (find_mapped_hostid_entry(idmap, 0, ID_TYPE_UID)) ++ maps_host_root = true; ++ ++ /* If new{g,u}idmap exists, that is, if shadow is handing out subuid ++ * ranges, then insist that root also reserve ranges in subuid. This ++ * will protected it by preventing another user from being handed the ++ * range by shadow. ++ */ ++ uidmap = idmaptool_on_path_and_privileged("newuidmap", CAP_SETUID); ++ if (uidmap == -ENOENT) ++ WARN("newuidmap binary is missing"); ++ else if (!uidmap) ++ WARN("newuidmap is lacking necessary privileges"); ++ ++ gidmap = idmaptool_on_path_and_privileged("newgidmap", CAP_SETGID); ++ if (gidmap == -ENOENT) ++ WARN("newgidmap binary is missing"); ++ else if (!gidmap) ++ WARN("newgidmap is lacking necessary privileges"); ++ ++ if (maps_host_root) { ++ INFO("Caller maps host root. Writing mapping directly"); ++ } else if (uidmap > 0 && gidmap > 0) { ++ DEBUG("Functional newuidmap and newgidmap binary found"); ++ use_shadow = true; ++ } else { ++ /* In case unprivileged users run application containers via ++ * execute() or a start*() there are valid cases where they may ++ * only want to map their own {g,u}id. Let's not block them from ++ * doing so by requiring geteuid() == 0. ++ */ ++ DEBUG("No newuidmap and newgidmap binary found. Trying to " ++ "write directly with euid %d", hostuid); ++ } ++ ++ /* Check if we really need to use newuidmap and newgidmap. ++ * If the user is only remapping their own {g,u}id, we don't need it. ++ */ ++ if (use_shadow && list_len(map, idmap, head) == 2) { ++ use_shadow = false; ++ list_for_each_entry(map, idmap, head) { ++ if (map->idtype == ID_TYPE_UID && map->range == 1 && ++ map->nsid == hostuid && map->hostid == hostuid) ++ continue; ++ if (map->idtype == ID_TYPE_GID && map->range == 1 && ++ map->nsid == hostgid && map->hostid == hostgid) ++ continue; ++ use_shadow = true; ++ break; ++ } ++ } ++ ++ for (type = ID_TYPE_UID, u_or_g = 'u'; type <= ID_TYPE_GID; ++ type++, u_or_g = 'g') { ++ pos = mapbuf; ++ ++ if (use_shadow) ++ pos += sprintf(mapbuf, "new%cidmap %d", u_or_g, pid); ++ ++ list_for_each_entry(map, idmap, head) { ++ if (map->idtype != type) ++ continue; ++ ++ had_entry = true; ++ ++ left = LXC_IDMAPLEN - (pos - mapbuf); ++ fill = strnprintf(pos, left, "%s%lu %lu %lu%s", ++ use_shadow ? " " : "", map->nsid, ++ map->hostid, map->range, ++ use_shadow ? "" : "\n"); ++ /* ++ * The kernel only takes <= 4k for writes to ++ * /proc//{g,u}id_map ++ */ ++ if (fill <= 0) ++ return log_error_errno(-1, errno, "Too many %cid mappings defined", u_or_g); ++ ++ pos += fill; ++ } ++ if (!had_entry) ++ continue; ++ ++ /* Try to catch the output of new{g,u}idmap to make debugging ++ * easier. ++ */ ++ if (use_shadow) { ++ ret = run_command(cmd_output, sizeof(cmd_output), ++ lxc_map_ids_exec_wrapper, ++ (void *)mapbuf); ++ if (ret < 0) ++ return log_error(-1, "new%cidmap failed to write mapping \"%s\": %s", u_or_g, cmd_output, mapbuf); ++ TRACE("new%cidmap wrote mapping \"%s\"", u_or_g, mapbuf); ++ } else { ++ ret = write_id_mapping(type, pid, mapbuf, pos - mapbuf); ++ if (ret < 0) ++ return log_error(-1, "Failed to write mapping: %s", mapbuf); ++ TRACE("Wrote mapping \"%s\"", mapbuf); ++ } ++ ++ memset(mapbuf, 0, sizeof(mapbuf)); ++ } ++ ++ return 0; ++} ++ ++int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype) ++{ ++ struct id_map *map; ++ ++ list_for_each_entry(map, &conf->id_map, head) { ++ if (map->idtype != idtype) ++ continue; ++ ++ if (id >= map->hostid && id < map->hostid + map->range) ++ return (id - map->hostid) + map->nsid; ++ } ++ ++ return -1; ++} ++ ++int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype) ++{ ++ struct id_map *map; ++ unsigned int freeid = 0; ++ ++again: ++ list_for_each_entry(map, &conf->id_map, head) { ++ if (map->idtype != idtype) ++ continue; ++ ++ if (freeid >= map->nsid && freeid < map->nsid + map->range) { ++ freeid = map->nsid + map->range; ++ goto again; ++ } ++ } ++ ++ return freeid; ++} ++ ++static const struct id_map *find_mapped_nsid_entry(const struct lxc_conf *conf, ++ unsigned id, ++ enum idtype idtype) ++{ ++ struct id_map *map; ++ struct id_map *retmap = NULL; ++ ++ /* Shortcut for container's root mappings. */ ++ if (id == 0) { ++ if (idtype == ID_TYPE_UID) ++ return conf->root_nsuid_map; ++ ++ if (idtype == ID_TYPE_GID) ++ return conf->root_nsgid_map; ++ } ++ ++ list_for_each_entry(map, &conf->id_map, head) { ++ if (map->idtype != idtype) ++ continue; ++ ++ if (id >= map->nsid && id < map->nsid + map->range) { ++ retmap = map; ++ break; ++ } ++ } ++ ++ return retmap; ++} ++ ++struct id_map *mapped_nsid_add(const struct lxc_conf *conf, unsigned id, ++ enum idtype idtype) ++{ ++ const struct id_map *map; ++ struct id_map *retmap; ++ ++ map = find_mapped_nsid_entry(conf, id, idtype); ++ if (!map) ++ return NULL; ++ ++ retmap = zalloc(sizeof(*retmap)); ++ if (!retmap) ++ return NULL; ++ ++ memcpy(retmap, map, sizeof(*retmap)); ++ return retmap; ++} ++ ++static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap, ++ unsigned id, enum idtype idtype) ++{ ++ struct id_map *retmap = NULL; ++ struct id_map *map; ++ ++ list_for_each_entry(map, idmap, head) { ++ if (map->idtype != idtype) ++ continue; ++ ++ if (id >= map->hostid && id < map->hostid + map->range) { ++ retmap = map; ++ break; ++ } ++ } ++ ++ return retmap; ++} ++ ++/* Allocate a new {g,u}id mapping for the given {g,u}id. Re-use an already ++ * existing one or establish a new one. ++ */ ++struct id_map *mapped_hostid_add(const struct lxc_conf *conf, uid_t id, ++ enum idtype type) ++{ ++ __do_free struct id_map *entry = NULL; ++ int hostid_mapped; ++ struct id_map *tmp = NULL; ++ ++ entry = zalloc(sizeof(*entry)); ++ if (!entry) ++ return NULL; ++ ++ /* Reuse existing mapping. */ ++ tmp = find_mapped_hostid_entry(&conf->id_map, id, type); ++ if (tmp) { ++ memcpy(entry, tmp, sizeof(*entry)); ++ } else { ++ /* Find new mapping. */ ++ hostid_mapped = find_unmapped_nsid(conf, type); ++ if (hostid_mapped < 0) ++ return log_debug(NULL, "Failed to find free mapping for id %d", id); ++ ++ entry->idtype = type; ++ entry->nsid = hostid_mapped; ++ entry->hostid = (unsigned long)id; ++ entry->range = 1; ++ } ++ ++ return move_ptr(entry); ++} +--- /dev/null ++++ b/src/lxc/idmap_utils.h +@@ -0,0 +1,30 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#ifndef __LXC_IDMAP_UTILS_H ++#define __LXC_IDMAP_UTILS_H ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "compiler.h" ++#include "conf.h" ++ ++__hidden extern int lxc_map_ids(struct list_head *idmap, pid_t pid); ++__hidden extern int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype); ++__hidden extern int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype); ++__hidden extern struct id_map *mapped_hostid_add(const struct lxc_conf *conf, uid_t id, ++ enum idtype type); ++__hidden extern struct id_map *mapped_nsid_add(const struct lxc_conf *conf, unsigned id, ++ enum idtype idtype); ++ ++__hidden extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size) ++ __access_r(3, 4); ++ ++#endif +--- a/src/lxc/lxccontainer.c ++++ b/src/lxc/lxccontainer.c +@@ -39,6 +39,7 @@ + #include "confile_utils.h" + #include "criu.h" + #include "error.h" ++#include "idmap_utils.h" + #include "initutils.h" + #include "log.h" + #include "lxc.h" +--- a/src/lxc/meson.build ++++ b/src/lxc/meson.build +@@ -86,6 +86,8 @@ liblxc_sources = files( + 'file_utils.h', + 'freezer.c', + 'hlist.h', ++ 'idmap_utils.c', ++ 'idmap_utils.h', + 'initutils.c', + 'initutils.h', + 'list.h', +--- a/src/lxc/start.c ++++ b/src/lxc/start.c +@@ -38,6 +38,7 @@ + #include "confile_utils.h" + #include "error.h" + #include "file_utils.h" ++#include "idmap_utils.h" + #include "list.h" + #include "log.h" + #include "lsm/lsm.h" +--- a/src/lxc/tools/lxc_unshare.c ++++ b/src/lxc/tools/lxc_unshare.c +@@ -21,6 +21,7 @@ + + #include "arguments.h" + #include "caps.h" ++#include "idmap_utils.h" + #include "list.h" + #include "log.h" + #include "namespace.h" diff --git a/utils/lxc/patches/005-conf-reorganize-split-code-to-utils.c.patch b/utils/lxc/patches/005-conf-reorganize-split-code-to-utils.c.patch new file mode 100644 index 0000000000..55cf89b549 --- /dev/null +++ b/utils/lxc/patches/005-conf-reorganize-split-code-to-utils.c.patch @@ -0,0 +1,469 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sun, 18 Feb 2024 15:24:29 +0100 +Subject: [PATCH] conf: reorganize/split code to utils.c + +Move run_script/run_script_argv helpers to utils.c + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit 9bb31888168eaa2ceb1302439aa638c7850a6841) +--- + src/lxc/conf.c | 201 ------------------------------------------------ + src/lxc/conf.h | 4 +- + src/lxc/utils.c | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ + src/lxc/utils.h | 4 + + 4 files changed, 206 insertions(+), 204 deletions(-) + +--- a/src/lxc/conf.c ++++ b/src/lxc/conf.c +@@ -285,207 +285,6 @@ static struct limit_opt limit_opt[] = { + #endif + }; + +-static int run_buffer(char *buffer) +-{ +- __do_free char *output = NULL; +- __do_lxc_pclose struct lxc_popen_FILE *f = NULL; +- int fd, ret; +- +- f = lxc_popen(buffer); +- if (!f) +- return log_error_errno(-1, errno, "Failed to popen() %s", buffer); +- +- output = zalloc(LXC_LOG_BUFFER_SIZE); +- if (!output) +- return log_error_errno(-1, ENOMEM, "Failed to allocate memory for %s", buffer); +- +- fd = fileno(f->f); +- if (fd < 0) +- return log_error_errno(-1, errno, "Failed to retrieve underlying file descriptor"); +- +- for (int i = 0; i < 10; i++) { +- ssize_t bytes_read; +- +- bytes_read = lxc_read_nointr(fd, output, LXC_LOG_BUFFER_SIZE - 1); +- if (bytes_read > 0) { +- output[bytes_read] = '\0'; +- DEBUG("Script %s produced output: %s", buffer, output); +- continue; +- } +- +- break; +- } +- +- ret = lxc_pclose(move_ptr(f)); +- if (ret == -1) +- return log_error_errno(-1, errno, "Script exited with error"); +- else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) +- return log_error(-1, "Script exited with status %d", WEXITSTATUS(ret)); +- else if (WIFSIGNALED(ret)) +- return log_error(-1, "Script terminated by signal %d", WTERMSIG(ret)); +- +- return 0; +-} +- +-int run_script_argv(const char *name, unsigned int hook_version, +- const char *section, const char *script, +- const char *hookname, char **argv) +-{ +- __do_free char *buffer = NULL; +- int buf_pos, i, ret; +- size_t size = 0; +- +- if (hook_version == 0) +- INFO("Executing script \"%s\" for container \"%s\", config section \"%s\"", +- script, name, section); +- else +- INFO("Executing script \"%s\" for container \"%s\"", script, name); +- +- for (i = 0; argv && argv[i]; i++) +- size += strlen(argv[i]) + 1; +- +- size += STRLITERALLEN("exec"); +- size++; +- size += strlen(script); +- size++; +- +- if (size > INT_MAX) +- return -EFBIG; +- +- if (hook_version == 0) { +- size += strlen(hookname); +- size++; +- +- size += strlen(name); +- size++; +- +- size += strlen(section); +- size++; +- +- if (size > INT_MAX) +- return -EFBIG; +- } +- +- buffer = zalloc(size); +- if (!buffer) +- return -ENOMEM; +- +- if (hook_version == 0) +- buf_pos = strnprintf(buffer, size, "exec %s %s %s %s", script, name, section, hookname); +- else +- buf_pos = strnprintf(buffer, size, "exec %s", script); +- if (buf_pos < 0) +- return log_error_errno(-1, errno, "Failed to create command line for script \"%s\"", script); +- +- if (hook_version == 1) { +- ret = setenv("LXC_HOOK_TYPE", hookname, 1); +- if (ret < 0) { +- return log_error_errno(-1, errno, "Failed to set environment variable: LXC_HOOK_TYPE=%s", hookname); +- } +- TRACE("Set environment variable: LXC_HOOK_TYPE=%s", hookname); +- +- ret = setenv("LXC_HOOK_SECTION", section, 1); +- if (ret < 0) +- return log_error_errno(-1, errno, "Failed to set environment variable: LXC_HOOK_SECTION=%s", section); +- TRACE("Set environment variable: LXC_HOOK_SECTION=%s", section); +- +- if (strequal(section, "net")) { +- char *parent; +- +- if (!argv || !argv[0]) +- return -1; +- +- ret = setenv("LXC_NET_TYPE", argv[0], 1); +- if (ret < 0) +- return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_TYPE=%s", argv[0]); +- TRACE("Set environment variable: LXC_NET_TYPE=%s", argv[0]); +- +- parent = argv[1] ? argv[1] : ""; +- +- if (strequal(argv[0], "macvlan")) { +- ret = setenv("LXC_NET_PARENT", parent, 1); +- if (ret < 0) +- return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_PARENT=%s", parent); +- TRACE("Set environment variable: LXC_NET_PARENT=%s", parent); +- } else if (strequal(argv[0], "phys")) { +- ret = setenv("LXC_NET_PARENT", parent, 1); +- if (ret < 0) +- return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_PARENT=%s", parent); +- TRACE("Set environment variable: LXC_NET_PARENT=%s", parent); +- } else if (strequal(argv[0], "veth")) { +- char *peer = argv[2] ? argv[2] : ""; +- +- ret = setenv("LXC_NET_PEER", peer, 1); +- if (ret < 0) +- return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_PEER=%s", peer); +- TRACE("Set environment variable: LXC_NET_PEER=%s", peer); +- +- ret = setenv("LXC_NET_PARENT", parent, 1); +- if (ret < 0) +- return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_PARENT=%s", parent); +- TRACE("Set environment variable: LXC_NET_PARENT=%s", parent); +- } +- } +- } +- +- for (i = 0; argv && argv[i]; i++) { +- size_t len = size - buf_pos; +- +- ret = strnprintf(buffer + buf_pos, len, " %s", argv[i]); +- if (ret < 0) +- return log_error_errno(-1, errno, "Failed to create command line for script \"%s\"", script); +- buf_pos += ret; +- } +- +- return run_buffer(buffer); +-} +- +-int run_script(const char *name, const char *section, const char *script, ...) +-{ +- __do_free char *buffer = NULL; +- int ret; +- char *p; +- va_list ap; +- size_t size = 0; +- +- INFO("Executing script \"%s\" for container \"%s\", config section \"%s\"", +- script, name, section); +- +- va_start(ap, script); +- while ((p = va_arg(ap, char *))) +- size += strlen(p) + 1; +- va_end(ap); +- +- size += STRLITERALLEN("exec"); +- size += strlen(script); +- size += strlen(name); +- size += strlen(section); +- size += 4; +- +- if (size > INT_MAX) +- return -1; +- +- buffer = must_realloc(NULL, size); +- ret = strnprintf(buffer, size, "exec %s %s %s", script, name, section); +- if (ret < 0) +- return -1; +- +- va_start(ap, script); +- while ((p = va_arg(ap, char *))) { +- int len = size - ret; +- int rc; +- rc = strnprintf(buffer + ret, len, " %s", p); +- if (rc < 0) { +- va_end(ap); +- return -1; +- } +- ret += rc; +- } +- va_end(ap); +- +- return run_buffer(buffer); +-} +- + int lxc_storage_prepare(struct lxc_conf *conf) + { + int ret; +--- a/src/lxc/conf.h ++++ b/src/lxc/conf.h +@@ -30,6 +30,7 @@ + #include "string_utils.h" + #include "syscall_wrappers.h" + #include "terminal.h" ++#include "utils.h" + + #if HAVE_SYS_RESOURCE_H + #include +@@ -619,9 +620,6 @@ __hidden extern void tmp_proc_unmount(st + __hidden extern void suggest_default_idmap(void); + __hidden extern FILE *make_anonymous_mount_file(const struct list_head *mount, + bool include_nesting_helpers); +-__hidden extern int run_script(const char *name, const char *section, const char *script, ...); +-__hidden extern int run_script_argv(const char *name, unsigned int hook_version, const char *section, +- const char *script, const char *hookname, char **argsin); + + __hidden extern bool has_cap(__u32 cap, struct lxc_conf *conf); + static inline bool lxc_wants_cap(__u32 cap, struct lxc_conf *conf) +--- a/src/lxc/utils.c ++++ b/src/lxc/utils.c +@@ -533,6 +533,207 @@ int lxc_pclose(struct lxc_popen_FILE *fp + return wstatus; + } + ++static int run_buffer(char *buffer) ++{ ++ __do_free char *output = NULL; ++ __do_lxc_pclose struct lxc_popen_FILE *f = NULL; ++ int fd, ret; ++ ++ f = lxc_popen(buffer); ++ if (!f) ++ return log_error_errno(-1, errno, "Failed to popen() %s", buffer); ++ ++ output = zalloc(LXC_LOG_BUFFER_SIZE); ++ if (!output) ++ return log_error_errno(-1, ENOMEM, "Failed to allocate memory for %s", buffer); ++ ++ fd = fileno(f->f); ++ if (fd < 0) ++ return log_error_errno(-1, errno, "Failed to retrieve underlying file descriptor"); ++ ++ for (int i = 0; i < 10; i++) { ++ ssize_t bytes_read; ++ ++ bytes_read = lxc_read_nointr(fd, output, LXC_LOG_BUFFER_SIZE - 1); ++ if (bytes_read > 0) { ++ output[bytes_read] = '\0'; ++ DEBUG("Script %s produced output: %s", buffer, output); ++ continue; ++ } ++ ++ break; ++ } ++ ++ ret = lxc_pclose(move_ptr(f)); ++ if (ret == -1) ++ return log_error_errno(-1, errno, "Script exited with error"); ++ else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) ++ return log_error(-1, "Script exited with status %d", WEXITSTATUS(ret)); ++ else if (WIFSIGNALED(ret)) ++ return log_error(-1, "Script terminated by signal %d", WTERMSIG(ret)); ++ ++ return 0; ++} ++ ++int run_script_argv(const char *name, unsigned int hook_version, ++ const char *section, const char *script, ++ const char *hookname, char **argv) ++{ ++ __do_free char *buffer = NULL; ++ int buf_pos, i, ret; ++ size_t size = 0; ++ ++ if (hook_version == 0) ++ INFO("Executing script \"%s\" for container \"%s\", config section \"%s\"", ++ script, name, section); ++ else ++ INFO("Executing script \"%s\" for container \"%s\"", script, name); ++ ++ for (i = 0; argv && argv[i]; i++) ++ size += strlen(argv[i]) + 1; ++ ++ size += STRLITERALLEN("exec"); ++ size++; ++ size += strlen(script); ++ size++; ++ ++ if (size > INT_MAX) ++ return -EFBIG; ++ ++ if (hook_version == 0) { ++ size += strlen(hookname); ++ size++; ++ ++ size += strlen(name); ++ size++; ++ ++ size += strlen(section); ++ size++; ++ ++ if (size > INT_MAX) ++ return -EFBIG; ++ } ++ ++ buffer = zalloc(size); ++ if (!buffer) ++ return -ENOMEM; ++ ++ if (hook_version == 0) ++ buf_pos = strnprintf(buffer, size, "exec %s %s %s %s", script, name, section, hookname); ++ else ++ buf_pos = strnprintf(buffer, size, "exec %s", script); ++ if (buf_pos < 0) ++ return log_error_errno(-1, errno, "Failed to create command line for script \"%s\"", script); ++ ++ if (hook_version == 1) { ++ ret = setenv("LXC_HOOK_TYPE", hookname, 1); ++ if (ret < 0) { ++ return log_error_errno(-1, errno, "Failed to set environment variable: LXC_HOOK_TYPE=%s", hookname); ++ } ++ TRACE("Set environment variable: LXC_HOOK_TYPE=%s", hookname); ++ ++ ret = setenv("LXC_HOOK_SECTION", section, 1); ++ if (ret < 0) ++ return log_error_errno(-1, errno, "Failed to set environment variable: LXC_HOOK_SECTION=%s", section); ++ TRACE("Set environment variable: LXC_HOOK_SECTION=%s", section); ++ ++ if (strequal(section, "net")) { ++ char *parent; ++ ++ if (!argv || !argv[0]) ++ return -1; ++ ++ ret = setenv("LXC_NET_TYPE", argv[0], 1); ++ if (ret < 0) ++ return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_TYPE=%s", argv[0]); ++ TRACE("Set environment variable: LXC_NET_TYPE=%s", argv[0]); ++ ++ parent = argv[1] ? argv[1] : ""; ++ ++ if (strequal(argv[0], "macvlan")) { ++ ret = setenv("LXC_NET_PARENT", parent, 1); ++ if (ret < 0) ++ return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_PARENT=%s", parent); ++ TRACE("Set environment variable: LXC_NET_PARENT=%s", parent); ++ } else if (strequal(argv[0], "phys")) { ++ ret = setenv("LXC_NET_PARENT", parent, 1); ++ if (ret < 0) ++ return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_PARENT=%s", parent); ++ TRACE("Set environment variable: LXC_NET_PARENT=%s", parent); ++ } else if (strequal(argv[0], "veth")) { ++ char *peer = argv[2] ? argv[2] : ""; ++ ++ ret = setenv("LXC_NET_PEER", peer, 1); ++ if (ret < 0) ++ return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_PEER=%s", peer); ++ TRACE("Set environment variable: LXC_NET_PEER=%s", peer); ++ ++ ret = setenv("LXC_NET_PARENT", parent, 1); ++ if (ret < 0) ++ return log_error_errno(-1, errno, "Failed to set environment variable: LXC_NET_PARENT=%s", parent); ++ TRACE("Set environment variable: LXC_NET_PARENT=%s", parent); ++ } ++ } ++ } ++ ++ for (i = 0; argv && argv[i]; i++) { ++ size_t len = size - buf_pos; ++ ++ ret = strnprintf(buffer + buf_pos, len, " %s", argv[i]); ++ if (ret < 0) ++ return log_error_errno(-1, errno, "Failed to create command line for script \"%s\"", script); ++ buf_pos += ret; ++ } ++ ++ return run_buffer(buffer); ++} ++ ++int run_script(const char *name, const char *section, const char *script, ...) ++{ ++ __do_free char *buffer = NULL; ++ int ret; ++ char *p; ++ va_list ap; ++ size_t size = 0; ++ ++ INFO("Executing script \"%s\" for container \"%s\", config section \"%s\"", ++ script, name, section); ++ ++ va_start(ap, script); ++ while ((p = va_arg(ap, char *))) ++ size += strlen(p) + 1; ++ va_end(ap); ++ ++ size += STRLITERALLEN("exec"); ++ size += strlen(script); ++ size += strlen(name); ++ size += strlen(section); ++ size += 4; ++ ++ if (size > INT_MAX) ++ return -1; ++ ++ buffer = must_realloc(NULL, size); ++ ret = strnprintf(buffer, size, "exec %s %s %s", script, name, section); ++ if (ret < 0) ++ return -1; ++ ++ va_start(ap, script); ++ while ((p = va_arg(ap, char *))) { ++ int len = size - ret; ++ int rc; ++ rc = strnprintf(buffer + ret, len, " %s", p); ++ if (rc < 0) { ++ va_end(ap); ++ return -1; ++ } ++ ret += rc; ++ } ++ va_end(ap); ++ ++ return run_buffer(buffer); ++} ++ + int randseed(bool srand_it) + { + __do_fclose FILE *f = NULL; +--- a/src/lxc/utils.h ++++ b/src/lxc/utils.h +@@ -75,6 +75,10 @@ static inline void __auto_lxc_pclose__(s + } + #define __do_lxc_pclose __attribute__((__cleanup__(__auto_lxc_pclose__))) + ++__hidden extern int run_script(const char *name, const char *section, const char *script, ...); ++__hidden extern int run_script_argv(const char *name, unsigned int hook_version, const char *section, ++ const char *script, const char *hookname, char **argsin); ++ + /* + * wait on a child we forked + */ diff --git a/utils/lxc/patches/006-confile-unhide-lxc_config_parse_arch-helper.patch b/utils/lxc/patches/006-confile-unhide-lxc_config_parse_arch-helper.patch new file mode 100644 index 0000000000..bdcd4e7859 --- /dev/null +++ b/utils/lxc/patches/006-confile-unhide-lxc_config_parse_arch-helper.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sun, 18 Feb 2024 15:43:20 +0100 +Subject: [PATCH] confile: unhide lxc_config_parse_arch() helper + +Looks safe enough to be available for liblxc users. + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit 42eeffcb05c468fd7b3a90eeda4a3abe9f26844b) +--- + src/lxc/confile.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/src/lxc/confile.h ++++ b/src/lxc/confile.h +@@ -88,7 +88,7 @@ extern void lxc_config_define_free(struc + * Parse personality of the container. Returns 0 if personality is valid, + * negative errno otherwise. + */ +-__hidden extern int lxc_config_parse_arch(const char *arch, signed long *persona); ++extern int lxc_config_parse_arch(const char *arch, signed long *persona); + + __hidden extern int lxc_clear_config_item(struct lxc_conf *c, const char *key); + diff --git a/utils/lxc/patches/007-storage_utils-unhide-and-rename-is_valid_storage_typ.patch b/utils/lxc/patches/007-storage_utils-unhide-and-rename-is_valid_storage_typ.patch new file mode 100644 index 0000000000..12fd6e0764 --- /dev/null +++ b/utils/lxc/patches/007-storage_utils-unhide-and-rename-is_valid_storage_typ.patch @@ -0,0 +1,47 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sun, 18 Feb 2024 15:56:47 +0100 +Subject: [PATCH] storage_utils: unhide and rename is_valid_storage_type to + lxc_is_valid_storage_type + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit 6eb0a73e22027ff84b0768da0c2aec4029b7d143) +--- + src/lxc/storage/storage_utils.c | 2 +- + src/lxc/storage/storage_utils.h | 2 +- + src/lxc/tools/lxc_create.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +--- a/src/lxc/storage/storage_utils.c ++++ b/src/lxc/storage/storage_utils.c +@@ -442,7 +442,7 @@ uint64_t get_fssize(char *s) + return ret; + } + +-bool is_valid_storage_type(const char *type) ++bool lxc_is_valid_storage_type(const char *type) + { + if (strcmp(type, "dir") == 0 || + strcmp(type, "btrfs") == 0 || +--- a/src/lxc/storage/storage_utils.h ++++ b/src/lxc/storage/storage_utils.h +@@ -37,7 +37,7 @@ __hidden extern const char *linkderef(co + __hidden extern bool unpriv_snap_allowed(struct lxc_storage *b, const char *t, bool snap, + bool maybesnap); + __hidden extern uint64_t get_fssize(char *s); +-__hidden extern bool is_valid_storage_type(const char *type); ++extern bool lxc_is_valid_storage_type(const char *type); + __hidden extern int storage_destroy_wrapper(void *data); + + #endif /* __LXC_STORAGE_UTILS_H */ +--- a/src/lxc/tools/lxc_create.c ++++ b/src/lxc/tools/lxc_create.c +@@ -236,7 +236,7 @@ int main(int argc, char *argv[]) + /* Final check whether the user gave use a valid bdev type. */ + if (strncmp(my_args.bdevtype, "best", strlen(my_args.bdevtype)) != 0 && + strncmp(my_args.bdevtype, "_unset", strlen(my_args.bdevtype)) != 0 && +- !is_valid_storage_type(my_args.bdevtype)) { ++ !lxc_is_valid_storage_type(my_args.bdevtype)) { + ERROR("%s is not a valid backing storage type", my_args.bdevtype); + exit(EXIT_FAILURE); + } diff --git a/utils/lxc/patches/008-storage_utils-move-get_fssize-to-utils.patch b/utils/lxc/patches/008-storage_utils-move-get_fssize-to-utils.patch new file mode 100644 index 0000000000..ebbec6f4f0 --- /dev/null +++ b/utils/lxc/patches/008-storage_utils-move-get_fssize-to-utils.patch @@ -0,0 +1,122 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sun, 18 Feb 2024 16:04:54 +0100 +Subject: [PATCH] storage_utils: move get_fssize to utils + +This helper is used in the lxc/tools and it's +fully independent of storage_utils code, let's move it +to utils.c + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit 9eee450d253650699a6f871695bfed1901679931) +--- + src/lxc/storage/storage_utils.c | 34 --------------------------------- + src/lxc/storage/storage_utils.h | 1 - + src/lxc/utils.c | 34 +++++++++++++++++++++++++++++++++ + src/lxc/utils.h | 2 ++ + 4 files changed, 36 insertions(+), 35 deletions(-) + +--- a/src/lxc/storage/storage_utils.c ++++ b/src/lxc/storage/storage_utils.c +@@ -408,40 +408,6 @@ bool unpriv_snap_allowed(struct lxc_stor + return false; + } + +-uint64_t get_fssize(char *s) +-{ +- uint64_t ret; +- char *end; +- +- ret = strtoull(s, &end, 0); +- if (end == s) { +- ERROR("Invalid blockdev size '%s', using default size", s); +- return 0; +- } +- +- while (isblank(*end)) +- end++; +- +- if (*end == '\0') { +- ret *= 1024ULL * 1024ULL; /* MB by default */ +- } else if (*end == 'b' || *end == 'B') { +- ret *= 1ULL; +- } else if (*end == 'k' || *end == 'K') { +- ret *= 1024ULL; +- } else if (*end == 'm' || *end == 'M') { +- ret *= 1024ULL * 1024ULL; +- } else if (*end == 'g' || *end == 'G') { +- ret *= 1024ULL * 1024ULL * 1024ULL; +- } else if (*end == 't' || *end == 'T') { +- ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; +- } else { +- ERROR("Invalid blockdev unit size '%c' in '%s', using default size", *end, s); +- return 0; +- } +- +- return ret; +-} +- + bool lxc_is_valid_storage_type(const char *type) + { + if (strcmp(type, "dir") == 0 || +--- a/src/lxc/storage/storage_utils.h ++++ b/src/lxc/storage/storage_utils.h +@@ -36,7 +36,6 @@ __hidden extern int find_fstype_cb(char + __hidden extern const char *linkderef(const char *path, char *dest); + __hidden extern bool unpriv_snap_allowed(struct lxc_storage *b, const char *t, bool snap, + bool maybesnap); +-__hidden extern uint64_t get_fssize(char *s); + extern bool lxc_is_valid_storage_type(const char *type); + __hidden extern int storage_destroy_wrapper(void *data); + +--- a/src/lxc/utils.c ++++ b/src/lxc/utils.c +@@ -2150,3 +2150,37 @@ int print_r(int fd, const char *path) + (st.st_mode & ~S_IFMT), st.st_uid, st.st_gid, maybe_empty(path)); + return ret; + } ++ ++uint64_t get_fssize(char *s) ++{ ++ uint64_t ret; ++ char *end; ++ ++ ret = strtoull(s, &end, 0); ++ if (end == s) { ++ ERROR("Invalid blockdev size '%s', using default size", s); ++ return 0; ++ } ++ ++ while (isblank(*end)) ++ end++; ++ ++ if (*end == '\0') { ++ ret *= 1024ULL * 1024ULL; /* MB by default */ ++ } else if (*end == 'b' || *end == 'B') { ++ ret *= 1ULL; ++ } else if (*end == 'k' || *end == 'K') { ++ ret *= 1024ULL; ++ } else if (*end == 'm' || *end == 'M') { ++ ret *= 1024ULL * 1024ULL; ++ } else if (*end == 'g' || *end == 'G') { ++ ret *= 1024ULL * 1024ULL * 1024ULL; ++ } else if (*end == 't' || *end == 'T') { ++ ret *= 1024ULL * 1024ULL * 1024ULL * 1024ULL; ++ } else { ++ ERROR("Invalid blockdev unit size '%c' in '%s', using default size", *end, s); ++ return 0; ++ } ++ ++ return ret; ++} +--- a/src/lxc/utils.h ++++ b/src/lxc/utils.h +@@ -248,6 +248,8 @@ __hidden extern int safe_mount_beneath_a + const char *fstype, unsigned int flags, const void *data); + __hidden __lxc_unused int print_r(int fd, const char *path); + ++__hidden extern uint64_t get_fssize(char *s); ++ + static inline int copy_struct_from_client(__u32 server_size, void *dst, + __u32 client_size, const void *src) + { diff --git a/utils/lxc/patches/009-network-use-IN_LIBLXC.patch b/utils/lxc/patches/009-network-use-IN_LIBLXC.patch new file mode 100644 index 0000000000..01b9bd7f56 --- /dev/null +++ b/utils/lxc/patches/009-network-use-IN_LIBLXC.patch @@ -0,0 +1,153 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sun, 18 Feb 2024 17:05:10 +0100 +Subject: [PATCH] network: use IN_LIBLXC + +Put a bunch of functions under #if IN_LIBLXC to compile-out +them when network.c is linked with tools/tests code. + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit b90fecfda1474edbbe68abeb0b392ebeabd7f8d7) +--- + src/lxc/network.c | 33 +++++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +--- a/src/lxc/network.c ++++ b/src/lxc/network.c +@@ -113,8 +113,13 @@ char *lxc_ifname_alnum_case_sensitive(ch + + return template; + } ++ ++#ifdef IN_LIBLXC ++ + static const char loop_device[] = "lo"; + ++#endif /* IN_LIBLXC */ ++ + static int lxc_ip_route_dest(__u16 nlmsg_type, int family, int ifindex, void *dest, unsigned int netmask) + { + call_cleaner(nlmsg_free) struct nlmsg *answer = NULL, *nlmsg = NULL; +@@ -172,6 +177,8 @@ static int lxc_ipv6_dest_add(int ifindex + return lxc_ip_route_dest(RTM_NEWROUTE, AF_INET6, ifindex, dest, netmask); + } + ++#ifdef IN_LIBLXC ++ + static int lxc_ipv4_dest_del(int ifindex, struct in_addr *dest, unsigned int netmask) + { + return lxc_ip_route_dest(RTM_DELROUTE, AF_INET, ifindex, dest, netmask); +@@ -1267,6 +1274,8 @@ static netdev_configure_server_cb netdev + [LXC_NET_NONE] = netdev_configure_server_none, + }; + ++#endif /* IN_LIBLXC */ ++ + static int __netdev_configure_container_common(struct lxc_netdev *netdev) + { + char current_ifname[IFNAMSIZ]; +@@ -1355,6 +1364,8 @@ static netdev_configure_container_cb net + [LXC_NET_NONE] = netdev_configure_container_none, + }; + ++#ifdef IN_LIBLXC ++ + static int netdev_shutdown_server_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) + { + int ret; +@@ -1496,6 +1507,8 @@ static netdev_shutdown_server_cb netdev_ + [LXC_NET_NONE] = netdev_shutdown_server_none, + }; + ++#endif /* IN_LIBLXC */ ++ + static int lxc_netdev_move_by_index_fd(int ifindex, int fd, const char *ifname) + { + call_cleaner(nlmsg_free) struct nlmsg *nlmsg = NULL; +@@ -2360,6 +2373,8 @@ static int neigh_proxy_set(const char *i + return proc_sys_net_write(path, flag ? "1" : "0"); + } + ++#ifdef IN_LIBLXC ++ + static int lxc_is_ip_neigh_proxy_enabled(const char *ifname, int family) + { + int ret; +@@ -2378,6 +2393,8 @@ static int lxc_is_ip_neigh_proxy_enabled + return lxc_read_file_expect(path, buf, 1, "1"); + } + ++#endif /* IN_LIBLXC */ ++ + int lxc_neigh_proxy_on(const char *name, int family) + { + return neigh_proxy_set(name, family, 1); +@@ -2901,6 +2918,8 @@ int lxc_find_gateway_addresses(struct lx + return 0; + } + ++#ifdef IN_LIBLXC ++ + #define LXC_USERNIC_PATH LIBEXECDIR "/lxc/lxc-user-nic" + static int lxc_create_network_unpriv_exec(const char *lxcpath, + const char *lxcname, +@@ -3430,6 +3449,8 @@ static int lxc_create_network_priv(struc + return 0; + } + ++#endif /* IN_LIBLXC */ ++ + /* + * LXC moves network devices into the target namespace based on their created + * name. The created name can either be randomly generated for e.g. veth +@@ -3567,6 +3588,8 @@ static int network_requires_advanced_set + return true; + } + ++#ifdef IN_LIBLXC ++ + static int lxc_create_network_unpriv(struct lxc_handler *handler) + { + int hooks_version = handler->conf->hooks_version; +@@ -3709,6 +3732,8 @@ clear_ifindices: + return true; + } + ++#endif /* IN_LIBLXC */ ++ + int lxc_requests_empty_network(struct lxc_handler *handler) + { + struct list_head *netdevs = &handler->conf->netdevs; +@@ -4152,6 +4177,8 @@ int lxc_network_recv_name_and_ifindex_fr + return 0; + } + ++#ifdef IN_LIBLXC ++ + void lxc_delete_network(struct lxc_handler *handler) + { + bool bret; +@@ -4173,6 +4200,8 @@ void lxc_delete_network(struct lxc_handl + DEBUG("Deleted network devices"); + } + ++#endif /* IN_LIBLXC */ ++ + int lxc_netns_set_nsid(int fd) + { + int ret; +@@ -4302,6 +4331,8 @@ int lxc_netns_get_nsid(int fd) + return -1; + } + ++#ifdef IN_LIBLXC ++ + int lxc_create_network(struct lxc_handler *handler) + { + int ret; +@@ -4316,3 +4347,5 @@ int lxc_create_network(struct lxc_handle + + return lxc_create_network_unpriv(handler); + } ++ ++#endif /* IN_LIBLXC */ diff --git a/utils/lxc/patches/010-meson-link-with-liblxc-dynamically-everywhere-if-pos.patch b/utils/lxc/patches/010-meson-link-with-liblxc-dynamically-everywhere-if-pos.patch new file mode 100644 index 0000000000..9b6eacd1a4 --- /dev/null +++ b/utils/lxc/patches/010-meson-link-with-liblxc-dynamically-everywhere-if-pos.patch @@ -0,0 +1,217 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Alexander Mikhalitsyn +Date: Sun, 18 Feb 2024 17:12:49 +0100 +Subject: [PATCH] meson: link with liblxc dynamically everywhere if possible + +Link tests/tools/commands dynamically with liblxc if possible. + +Signed-off-by: Alexander Mikhalitsyn +(cherry picked from commit 86799f55422f31a0536c95639fe4b78fa7aa780f) +--- + src/lxc/cmd/meson.build | 5 +++-- + src/lxc/meson.build | 43 +++++++++++++++++++++++++++++++++++++++ + src/lxc/tools/meson.build | 26 ++++++++++++++++++----- + src/tests/meson.build | 20 +++++++++++------- + 4 files changed, 80 insertions(+), 14 deletions(-) + +--- a/src/lxc/cmd/meson.build ++++ b/src/lxc/cmd/meson.build +@@ -1,6 +1,6 @@ + # SPDX-License-Identifier: LGPL-2.1-or-later + +-cmd_common_sources = liblxc_sources + include_sources ++cmd_common_sources = liblxc_ext_sources + include_sources + + cmd_lxc_init_sources = files( + 'lxc_init.c', +@@ -46,7 +46,7 @@ cmd_lxc_init_static_sources = files( + '../string_utils.c', + '../string_utils.h') + include_sources + +-cmd_lxc_monitord_sources = files('lxc_monitord.c') + cmd_common_sources + netns_ifaddrs_sources ++cmd_lxc_monitord_sources = files('lxc_monitord.c') + include_sources + netns_ifaddrs_sources + cmd_lxc_user_nic_sources = files('lxc_user_nic.c') + cmd_common_sources + netns_ifaddrs_sources + cmd_lxc_usernsexec_sources = files('lxc_usernsexec.c') + cmd_common_sources + netns_ifaddrs_sources + +@@ -89,6 +89,7 @@ cmd_programs += executable( + cmd_lxc_monitord_sources, + include_directories: liblxc_includes, + dependencies: liblxc_dep, ++ link_with: [liblxc_static], + install: true, + install_dir: lxclibexec) + +--- a/src/lxc/meson.build ++++ b/src/lxc/meson.build +@@ -141,6 +141,49 @@ liblxc_sources = files( + 'uuid.c', + 'uuid.h') + ++# subset of liblxc sources for internal users like tools/commands/tests ++liblxc_ext_sources = files( ++ 'caps.c', ++ 'caps.h', ++ 'compiler.h', ++ 'error.c', ++ 'error.h', ++ 'error_utils.h', ++ 'file_utils.c', ++ 'file_utils.h', ++ 'hlist.h', ++ 'idmap_utils.c', ++ 'idmap_utils.h', ++ 'initutils.c', ++ 'initutils.h', ++ 'list.h', ++ 'log.c', ++ 'log.h', ++ 'mainloop.c', ++ 'mainloop.h', ++ 'namespace.c', ++ 'namespace.h', ++ 'network.c', ++ 'network.h', ++ 'nl.c', ++ 'nl.h', ++ 'parse.c', ++ 'parse.h', ++ 'open_utils.h', ++ 'rexec.c', ++ 'rexec.h', ++ 'rtnl.c', ++ 'rtnl.h', ++ 'open_utils.h', ++ 'process_utils.c', ++ 'process_utils.h', ++ 'string_utils.c', ++ 'string_utils.h', ++ 'syscall_numbers.h', ++ 'syscall_wrappers.h', ++ 'utils.c', ++ 'utils.h') ++ + if want_apparmor and libapparmor.found() + liblxc_sources += files('lsm/apparmor.c') + endif +--- a/src/lxc/tools/meson.build ++++ b/src/lxc/tools/meson.build +@@ -1,19 +1,35 @@ + # SPDX-License-Identifier: LGPL-2.1-or-later + +-tools_common_sources = liblxc_sources + files('arguments.c', 'arguments.h') + include_sources + netns_ifaddrs_sources ++tools_common_sources = files('arguments.c', 'arguments.h') + include_sources + netns_ifaddrs_sources + +-tools_commands = ['attach', 'autostart', 'cgroup', 'checkpoint', 'config', ++tools_commands_dynamic_link = ['attach', 'autostart', 'cgroup', 'checkpoint', 'config', + 'console', 'copy', 'create', 'destroy', 'device', 'execute', 'freeze', +- 'info', 'ls', 'monitor', 'snapshot', 'start', 'stop', 'top', 'unfreeze', ++ 'info', 'ls', 'snapshot', 'start', 'stop', 'top', 'unfreeze', + 'unshare', 'wait'] + ++tools_commands_static_link = ['monitor'] ++ ++tools_commands = tools_commands_dynamic_link + tools_commands_static_link ++ + if want_tools +- foreach cmd : tools_commands ++ foreach cmd : tools_commands_dynamic_link ++ public_programs += executable( ++ 'lxc-' + cmd, ++ files('lxc_' + cmd + '.c') + tools_common_sources + liblxc_ext_sources, ++ dependencies: liblxc_dependencies, ++ include_directories: liblxc_includes, ++ c_args: ['-DNO_LXC_CONF'], ++ link_with: [liblxc], ++ install: true) ++ endforeach ++ ++ foreach cmd : tools_commands_static_link + public_programs += executable( + 'lxc-' + cmd, + files('lxc_' + cmd + '.c') + tools_common_sources, ++ dependencies: liblxc_dependencies, + include_directories: liblxc_includes, +- dependencies: liblxc_dep, ++ link_with: [liblxc_static], + install: true) + endforeach + endif +--- a/src/tests/meson.build ++++ b/src/tests/meson.build +@@ -1,6 +1,6 @@ + # SPDX-License-Identifier: LGPL-2.1-or-later + +-tests_common_sources = liblxc_sources + include_sources + netns_ifaddrs_sources ++tests_common_sources = liblxc_ext_sources + include_sources + netns_ifaddrs_sources + + if want_tests + test_programs += executable( +@@ -26,9 +26,10 @@ if want_tests + + test_programs += executable( + 'lxc-test-attach', +- files('attach.c') + tests_common_sources, ++ files('attach.c'), + include_directories: liblxc_includes, + dependencies: liblxc_dep, ++ link_with: [liblxc_static], + install: true) + + test_programs += executable( +@@ -47,9 +48,10 @@ if want_tests + + test_programs += executable( + 'lxc-test-cgpath', +- files('cgpath.c') + tests_common_sources, ++ files('cgpath.c'), + include_directories: liblxc_includes, + dependencies: liblxc_dep, ++ link_with: [liblxc_static], + install: true) + + test_programs += executable( +@@ -68,9 +70,10 @@ if want_tests + + test_programs += executable( + 'lxc-test-config-jump-table', +- files('config_jump_table.c') + tests_common_sources, ++ files('config_jump_table.c'), + include_directories: liblxc_includes, + dependencies: liblxc_dep, ++ link_with: [liblxc_static], + install: true) + + test_programs += executable( +@@ -152,16 +155,18 @@ if want_tests + + test_programs += executable( + 'lxc-test-locktests', +- files('locktests.c') + tests_common_sources, ++ files('locktests.c'), + include_directories: liblxc_includes, + dependencies: liblxc_dep, ++ link_with: [liblxc_static], + install: true) + + test_programs += executable( + 'lxc-test-utils', +- files('lxc-test-utils.c') + tests_common_sources, ++ files('lxc-test-utils.c'), + include_directories: liblxc_includes, + dependencies: liblxc_dep, ++ link_with: [liblxc_static], + install: true) + + test_programs += executable( +@@ -194,9 +199,10 @@ if want_tests + + test_programs += executable( + 'lxc-test-parse-config-file', +- files('parse_config_file.c') + tests_common_sources, ++ files('parse_config_file.c'), + include_directories: liblxc_includes, + dependencies: liblxc_dep, ++ link_with: [liblxc_static], + install: true) + + test_programs += executable(