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/
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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.
+
+(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;
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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.
+
+(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)
+ {
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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...
+
+(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
+ /*
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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.
+
+(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/<pid>/{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 <errno.h>
++#include <fcntl.h>
++#include <malloc.h>
++#include <pthread.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <sys/file.h>
++#include <unistd.h>
++
++#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/<pid>/{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 <fcntl.h>
++#include <semaphore.h>
++#include <string.h>
++#include <sys/file.h>
++#include <sys/stat.h>
++#include <time.h>
++#include <unistd.h>
++
++#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"
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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
+
+(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 <sys/resource.h>
+@@ -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
+ */
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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.
+
+(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);
+
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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
+
+(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);
+ }
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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
+
+(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)
+ {
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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.
+
+(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 */
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+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.
+
+(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(