From 3793183a6e2ee96ece4a268b2cf09e8282666071 Mon Sep 17 00:00:00 2001 From: ComixHe Date: Mon, 13 Apr 2026 13:12:27 +0800 Subject: [PATCH] refactor: optimizing the config parsing process Signed-off-by: ComixHe --- src/linyaps_box/config.cpp | 658 +++++++++++++++------------------ src/linyaps_box/config.h | 5 +- src/linyaps_box/container.cpp | 9 +- src/linyaps_box/utils/file.cpp | 46 ++- src/linyaps_box/utils/file.h | 6 +- 5 files changed, 345 insertions(+), 379 deletions(-) diff --git a/src/linyaps_box/config.cpp b/src/linyaps_box/config.cpp index 1630233..42fe5bd 100644 --- a/src/linyaps_box/config.cpp +++ b/src/linyaps_box/config.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -11,68 +11,67 @@ namespace { -// This function is used to parse the mount options from the config file and it only will be called -// once. +// Maps are static to avoid re-construction on every mount entry. +static const std::unordered_map propagation_flags_map{ + { "rprivate", MS_PRIVATE | MS_REC }, { "private", MS_PRIVATE }, + { "rslave", MS_SLAVE | MS_REC }, { "slave", MS_SLAVE }, + { "rshared", MS_SHARED | MS_REC }, { "shared", MS_SHARED }, + { "runbindable", MS_UNBINDABLE | MS_REC }, { "unbindable", MS_UNBINDABLE }, +}; + +static const std::unordered_map flags_map{ + { "bind", MS_BIND }, + { "defaults", 0 }, + { "dirsync", MS_DIRSYNC }, + { "iversion", MS_I_VERSION }, + { "lazytime", MS_LAZYTIME }, + { "mand", MS_MANDLOCK }, + { "noatime", MS_NOATIME }, + { "nodev", MS_NODEV }, + { "nodiratime", MS_NODIRATIME }, + { "noexec", MS_NOEXEC }, + { "nosuid", MS_NOSUID }, + { "nosymfollow", LINGYAPS_MS_NOSYMFOLLOW }, + { "rbind", MS_BIND | MS_REC }, + { "relatime", MS_RELATIME }, + { "remount", MS_REMOUNT }, + { "ro", MS_RDONLY }, + { "silent", MS_SILENT }, + { "strictatime", MS_STRICTATIME }, + { "sync", MS_SYNCHRONOUS }, +}; + +static const std::unordered_map unset_flags_map{ + { "async", MS_SYNCHRONOUS }, + { "atime", MS_NOATIME }, + { "dev", MS_NODEV }, + { "diratime", MS_NODIRATIME }, + { "exec", MS_NOEXEC }, + { "loud", MS_SILENT }, + { "noiversion", MS_I_VERSION }, + { "nolazytime", MS_LAZYTIME }, + { "nomand", MS_MANDLOCK }, + { "norelatime", MS_RELATIME }, + { "nostrictatime", MS_STRICTATIME }, + { "rw", MS_RDONLY }, + { "suid", MS_NOSUID }, + { "symfollow", LINGYAPS_MS_NOSYMFOLLOW }, +}; + +static const std::unordered_map + extra_flags_map{ + { "copy-symlink", linyaps_box::config::mount_t::extension::COPY_SYMLINK }, + }; + +// NOLINTEND(cert-err58-cpp) + auto parse_mount_options(const std::vector &options) -> std:: tuple { - const std::unordered_map propagation_flags_map{ - { "rprivate", MS_PRIVATE | MS_REC }, { "private", MS_PRIVATE }, - { "rslave", MS_SLAVE | MS_REC }, { "slave", MS_SLAVE }, - { "rshared", MS_SHARED | MS_REC }, { "shared", MS_SHARED }, - { "runbindable", MS_UNBINDABLE | MS_REC }, { "unbindable", MS_UNBINDABLE }, - }; - - const std::unordered_map flags_map{ - { "bind", MS_BIND }, - { "defaults", 0 }, - { "dirsync", MS_DIRSYNC }, - { "iversion", MS_I_VERSION }, - { "lazytime", MS_LAZYTIME }, - { "mand", MS_MANDLOCK }, - { "noatime", MS_NOATIME }, - { "nodev", MS_NODEV }, - { "nodiratime", MS_NODIRATIME }, - { "noexec", MS_NOEXEC }, - { "nosuid", MS_NOSUID }, - { "nosymfollow", LINGYAPS_MS_NOSYMFOLLOW }, - { "rbind", MS_BIND | MS_REC }, - { "relatime", MS_RELATIME }, - { "remount", MS_REMOUNT }, - { "ro", MS_RDONLY }, - { "silent", MS_SILENT }, - { "strictatime", MS_STRICTATIME }, - { "sync", MS_SYNCHRONOUS }, - }; - - const std::unordered_map unset_flags_map{ - { "async", MS_SYNCHRONOUS }, - { "atime", MS_NOATIME }, - { "dev", MS_NODEV }, - { "diratime", MS_NODIRATIME }, - { "exec", MS_NOEXEC }, - { "loud", MS_SILENT }, - { "noiversion", MS_I_VERSION }, - { "nolazytime", MS_LAZYTIME }, - { "nomand", MS_MANDLOCK }, - { "norelatime", MS_RELATIME }, - { "nostrictatime", MS_STRICTATIME }, - { "rw", MS_RDONLY }, - { "suid", MS_NOSUID }, - { "symfollow", LINGYAPS_MS_NOSYMFOLLOW }, - }; - - const std::unordered_map - extra_flags_map{ { "copy-symlink", - linyaps_box::config::mount_t::extension::COPY_SYMLINK } }; - unsigned long flags = 0; - linyaps_box::config::mount_t::extension extra_flags{ - linyaps_box::config::mount_t::extension::NONE - }; - + auto extra_flags = linyaps_box::config::mount_t::extension::NONE; unsigned long propagation_flags = 0; - std::stringstream data; + std::string data; for (const auto &opt : options) { if (auto it = flags_map.find(opt); it != flags_map.end()) { @@ -87,388 +86,319 @@ auto parse_mount_options(const std::vector &options) -> std:: propagation_flags |= it->second; continue; } - if (auto it = extra_flags_map.find(opt); it != extra_flags_map.end()) { extra_flags |= it->second; continue; } - data << "," << opt; + if (!data.empty()) { + data += ','; + } + data += opt; } - auto str = data.str(); - if (!str.empty()) { - str = str.substr(1); + + return { flags, propagation_flags, extra_flags, std::move(data) }; +} + +static const std::unordered_map + namespace_type_map{ + { "pid", linyaps_box::config::linux_t::namespace_t::type::PID }, + { "network", linyaps_box::config::linux_t::namespace_t::type::NET }, + { "ipc", linyaps_box::config::linux_t::namespace_t::type::IPC }, + { "uts", linyaps_box::config::linux_t::namespace_t::type::UTS }, + { "mount", linyaps_box::config::linux_t::namespace_t::type::MOUNT }, + { "user", linyaps_box::config::linux_t::namespace_t::type::USER }, + { "cgroup", linyaps_box::config::linux_t::namespace_t::type::CGROUP }, + }; + +static const std::unordered_map rootfs_propagation_map{ + { "shared", MS_SHARED }, + { "slave", MS_SLAVE }, + { "private", MS_PRIVATE }, + { "unbindable", MS_UNBINDABLE }, +}; + +} // namespace + +// ADL-based from_json overloads in the linyaps_box namespace. +// nlohmann_json discovers these via argument-dependent lookup when calling j.get(). +namespace linyaps_box { + +void from_json(const nlohmann::json &j, config::process_t::console_size_t &v) +{ + j.at("height").get_to(v.height); + j.at("width").get_to(v.width); +} + +void from_json(const nlohmann::json &j, config::process_t::rlimit_t &v) +{ + if (!j.contains("type")) { + throw std::runtime_error("rlimit must contain type"); } + j.at("type").get_to(v.type); + j.at("soft").get_to(v.soft); + j.at("hard").get_to(v.hard); +} - return { flags, propagation_flags, extra_flags, str }; +void from_json(const nlohmann::json &j, config::process_t::user_t &v) +{ + j.at("uid").get_to(v.uid); + j.at("gid").get_to(v.gid); + + if (auto it = j.find("umask"); it != j.end()) { + v.umask = it->get(); + } + + if (auto it = j.find("additionalGids"); it != j.end()) { + v.additional_gids = it->get>(); + } } #ifdef LINYAPS_BOX_ENABLE_CAP -linyaps_box::config::process_t::capabilities_t -parse_capability(const nlohmann::json &obj, const nlohmann::json::json_pointer &ptr) +void from_json(const nlohmann::json &j, config::process_t::capabilities_t &v) { - auto parse_cap_set = [&obj, &ptr](const char *set_name) { - const auto set = ptr / set_name; - std::vector cap_list; - if (!obj.contains(set)) { - return cap_list; + auto parse_cap_set = [&j](const char *name) -> std::vector { + auto it = j.find(name); + if (it == j.end()) { + return { }; } - const auto vec = obj[set].get>(); - std::for_each(vec.cbegin(), vec.cend(), [&cap_list](const std::string &cap) { + std::vector result; + result.reserve(it->size()); + for (const auto &elem : *it) { cap_value_t val{ 0 }; - if (cap_from_name(cap.c_str(), &val) < 0) { - throw std::runtime_error("unknown capability: " + cap); + auto cap_name = elem.get_ref(); + if (cap_from_name(cap_name.c_str(), &val) < 0) { + throw std::runtime_error("unknown capability: " + cap_name); } - - cap_list.push_back(val); - }); - - return cap_list; + result.push_back(val); + } + return result; }; - linyaps_box::config::process_t::capabilities_t cap{}; - cap.effective = parse_cap_set("effective"); - cap.ambient = parse_cap_set("ambient"); - cap.bounding = parse_cap_set("bounding"); - cap.inheritable = parse_cap_set("inheritable"); - cap.permitted = parse_cap_set("permitted"); - - return cap; + v.effective = parse_cap_set("effective"); + v.ambient = parse_cap_set("ambient"); + v.bounding = parse_cap_set("bounding"); + v.inheritable = parse_cap_set("inheritable"); + v.permitted = parse_cap_set("permitted"); } #endif -linyaps_box::config::process_t::rlimits_t parse_rlimits(const nlohmann::json &obj, - const nlohmann::json::json_pointer &ptr) +void from_json(const nlohmann::json &j, config::process_t &v) { - const auto &vec = obj[ptr]; - if (!vec.is_array()) { - throw std::runtime_error("rlimits must be an array"); - } - - linyaps_box::config::process_t::rlimits_t ret{}; - std::transform( - vec.cbegin(), - vec.cend(), - std::back_inserter(ret), - [](const nlohmann::json &json) { - if (!json.is_object()) { - throw std::runtime_error("rlimit must be an object"); - } - - if (!json.contains("type")) { - throw std::runtime_error("rlimit must contain type"); - } - - return linyaps_box::config::process_t::rlimit_t{ json["type"].get(), - json["soft"].get(), - json["hard"].get() }; - }); - return ret; -} + if (auto it = j.find("terminal"); it != j.end()) { + it->get_to(v.terminal); + } -auto parse_linux(const nlohmann::json &obj, const nlohmann::json::json_pointer &ptr) - -> linyaps_box::config::linux_t -{ - auto linux = linyaps_box::config::linux_t{}; - if (auto uid_ptr = ptr / "uidMappings"; obj.contains(uid_ptr)) { - const auto &vec = obj[uid_ptr]; - std::vector uid_mappings; - std::transform(vec.cbegin(), - vec.cend(), - std::back_inserter(uid_mappings), - [](const nlohmann::json &json) { - return linyaps_box::config::linux_t::id_mapping_t{ - json["hostID"].get(), - json["containerID"].get(), - json["size"].get(), - }; - }); - linux.uid_mappings = std::move(uid_mappings); - } - - if (auto gid_ptr = ptr / "gidMappings"; obj.contains(gid_ptr)) { - const auto &vec = obj[gid_ptr]; - std::vector gid_mappings; - std::transform(vec.cbegin(), - vec.cend(), - std::back_inserter(gid_mappings), - [](const nlohmann::json &json) { - return linyaps_box::config::linux_t::id_mapping_t{ - json["hostID"].get(), - json["containerID"].get(), - json["size"].get(), - }; - }); - linux.gid_mappings = std::move(gid_mappings); - } - - if (auto namespace_ptr = ptr / "namespaces"; obj.contains(namespace_ptr)) { - auto map_fn = [](const nlohmann::json &json) { - if (!json.contains("type")) { - throw std::runtime_error("property `type` is REQUIRED for linux namespaces"); - } + // https://github.com/opencontainers/runtime-spec/blob/09fcb39bb7185b46dfb206bc8f3fea914c674779/config.md?plain=1#L245 + if (v.terminal) { + if (auto it = j.find("consoleSize"); it != j.end()) { + v.console_size = it->get(); + } + } - linyaps_box::config::linux_t::namespace_t n; - auto type = json["type"].get(); - if (type == "pid") { - n.type_ = linyaps_box::config::linux_t::namespace_t::type::PID; - } else if (type == "network") { - n.type_ = linyaps_box::config::linux_t::namespace_t::type::NET; - } else if (type == "ipc") { - n.type_ = linyaps_box::config::linux_t::namespace_t::type::IPC; - } else if (type == "uts") { - n.type_ = linyaps_box::config::linux_t::namespace_t::type::UTS; - } else if (type == "mount") { - n.type_ = linyaps_box::config::linux_t::namespace_t::type::MOUNT; - } else if (type == "user") { - n.type_ = linyaps_box::config::linux_t::namespace_t::type::USER; - } else if (type == "cgroup") { - n.type_ = linyaps_box::config::linux_t::namespace_t::type::CGROUP; - } else { - throw std::runtime_error("unsupported namespace type: " + type); - } + j.at("cwd").get_to(v.cwd); - if (json.contains("path")) { - n.path = json["path"].get(); + if (auto it = j.find("env"); it != j.end()) { + auto env = it->get>(); + for (const auto &e : env) { + if (e.find('=') == std::string::npos) { + throw std::runtime_error("invalid env entry: " + e); } + } + v.env = std::move(env); + } - return n; - }; + j.at("args").get_to(v.args); - const auto &vec = obj[namespace_ptr]; - std::vector ns; - std::transform(vec.cbegin(), vec.cend(), std::back_inserter(ns), map_fn); - - linux.namespaces = std::move(ns); - } - - if (auto masked_path = ptr / "maskedPaths"; obj.contains(masked_path)) { - const auto &vec = obj[masked_path]; - std::vector masked_paths; - std::transform(vec.cbegin(), - vec.cend(), - std::back_inserter(masked_paths), - [](const nlohmann::json &json) { - return json.get(); - }); - linux.masked_paths = std::move(masked_paths); - } - - if (auto readonly_path = ptr / "readonlyPaths"; obj.contains(readonly_path)) { - const auto &vec = obj[readonly_path]; - std::vector readonly_paths; - std::transform(vec.cbegin(), - vec.cend(), - std::back_inserter(readonly_paths), - [](const nlohmann::json &json) { - return json.get(); - }); - linux.readonly_paths = std::move(readonly_paths); - } - - if (auto rootfs_propagation = ptr / "rootfsPropagation"; obj.contains(rootfs_propagation)) { - auto val = obj[rootfs_propagation].get(); - if (val == "shared") { - linux.rootfs_propagation = MS_SHARED; - } else if (val == "slave") { - linux.rootfs_propagation = MS_SLAVE; - } else if (val == "private") { - linux.rootfs_propagation = MS_PRIVATE; - } else if (val == "unbindable") { - linux.rootfs_propagation = MS_UNBINDABLE; - } else { - throw std::runtime_error("unsupported rootfs propagation: " + val); - } + if (auto it = j.find("rlimits"); it != j.end()) { + v.rlimits = it->get(); + } + + // TODO: apparmorProfile +#ifdef LINYAPS_BOX_ENABLE_CAP + if (auto it = j.find("capabilities"); it != j.end()) { + v.capabilities = it->get(); + } +#endif + + if (auto it = j.find("noNewPrivileges"); it != j.end()) { + it->get_to(v.no_new_privileges); } - return linux; + if (auto it = j.find("oomScoreAdj"); it != j.end()) { + v.oom_score_adj = it->get(); + } + + j.at("user").get_to(v.user); } -auto parse_1_2_0(const nlohmann::json &j) -> linyaps_box::config +void from_json(const nlohmann::json &j, config::linux_t::id_mapping_t &v) { - const auto ptr = ""_json_pointer; + j.at("hostID").get_to(v.host_id); + j.at("containerID").get_to(v.container_id); + j.at("size").get_to(v.size); +} - auto semver = linyaps_box::utils::semver(j[ptr / "ociVersion"].get()); - if (!linyaps_box::utils::semver(linyaps_box::config::oci_version).is_compatible_with(semver)) { - throw std::runtime_error("unsupported OCI version: " + semver.to_string()); +void from_json(const nlohmann::json &j, config::linux_t::namespace_t &v) +{ + auto type_str = j.at("type").get_ref(); + auto it = namespace_type_map.find(type_str); + if (it == namespace_type_map.end()) { + throw std::runtime_error("unsupported namespace type: " + type_str); } + v.type_ = it->second; - linyaps_box::config cfg; + if (auto path_it = j.find("path"); path_it != j.end()) { + v.path = path_it->get(); + } +} - { - if (j.contains(ptr / "process" / "terminal")) { - cfg.process.terminal = j[ptr / "process" / "terminal"].get(); - } +void from_json(const nlohmann::json &j, config::linux_t &v) +{ + if (auto it = j.find("uidMappings"); it != j.end()) { + v.uid_mappings = it->get>(); + } - // https://github.com/opencontainers/runtime-spec/blob/09fcb39bb7185b46dfb206bc8f3fea914c674779/config.md?plain=1#L245 - if (cfg.process.terminal && j.contains(ptr / "process" / "consoleSize")) { - cfg.process.console_size = linyaps_box::config::process_t::console_size_t{ - j[ptr / "process" / "consoleSize" / "height"].get(), - j[ptr / "process" / "consoleSize" / "width"].get() - }; - } + if (auto it = j.find("gidMappings"); it != j.end()) { + v.gid_mappings = it->get>(); + } - cfg.process.cwd = j[ptr / "process" / "cwd"].get(); + if (auto it = j.find("namespaces"); it != j.end()) { + v.namespaces = it->get>(); + } - if (j.contains(ptr / "process" / "env")) { - auto env = j[ptr / "process" / "env"].get>(); - for (const auto &e : env) { - auto pos = e.find('='); - if (pos == std::string::npos) { - throw std::runtime_error("invalid env entry: " + e); - } - } + if (auto it = j.find("maskedPaths"); it != j.end()) { + v.masked_paths = it->get>(); + } + + if (auto it = j.find("readonlyPaths"); it != j.end()) { + v.readonly_paths = it->get>(); + } - cfg.process.env = std::move(env); + if (auto it = j.find("rootfsPropagation"); it != j.end()) { + auto val = it->get_ref(); + auto prop_it = rootfs_propagation_map.find(val); + if (prop_it == rootfs_propagation_map.end()) { + throw std::runtime_error("unsupported rootfs propagation: " + val); } + v.rootfs_propagation = prop_it->second; + } +} - cfg.process.args = j[ptr / "process" / "args"].get>(); +void from_json(const nlohmann::json &j, config::hooks_t::hook_t &v) +{ + j.at("path").get_to(v.path); + if (!v.path.is_absolute()) { + throw std::runtime_error("hook path must be absolute"); + } - if (auto rlimits = ptr / "process" / "rlimits"; j.contains(rlimits)) { - cfg.process.rlimits = parse_rlimits(j, rlimits); - } + if (auto it = j.find("args"); it != j.end()) { + v.args = it->get>(); + } - // TODO: apparmorProfile -#ifdef LINYAPS_BOX_ENABLE_CAP - if (auto cap = ptr / "process" / "capabilities"; j.contains(cap)) { - cfg.process.capabilities = parse_capability(j, cap); + if (auto it = j.find("env"); it != j.end()) { + std::unordered_map env; + for (const auto &e : it->get>()) { + auto pos = e.find('='); + if (pos == std::string::npos) { + throw std::runtime_error("invalid env entry: " + e); + } + env[e.substr(0, pos)] = e.substr(pos + 1); } -#endif + v.env = std::move(env); + } - if (j.contains(ptr / "process" / "noNewPrivileges")) { - cfg.process.no_new_privileges = j[ptr / "process" / "noNewPrivileges"].get(); + if (auto it = j.find("timeout"); it != j.end()) { + v.timeout = it->get(); + if (v.timeout.value() <= 0) { + throw std::runtime_error("hook timeout must be greater than zero"); } + } +} - if (j.contains(ptr / "process" / "oomScoreAdj")) { - cfg.process.oom_score_adj = j[ptr / "process" / "oomScoreAdj"].get(); +void from_json(const nlohmann::json &j, config::hooks_t &v) +{ + auto get_hooks = [&j](const char *key) -> std::optional> { + auto it = j.find(key); + if (it == j.end()) { + return std::nullopt; } + return it->get>(); + }; - cfg.process.user.uid = j[ptr / "process" / "user" / "uid"].get(); - cfg.process.user.gid = j[ptr / "process" / "user" / "gid"].get(); - - if (j.contains(ptr / "process" / "user" / "umask")) { - cfg.process.user.umask = j[ptr / "process" / "user" / "umask"].get(); - } + v.prestart = get_hooks("prestart"); + v.create_runtime = get_hooks("createRuntime"); + v.create_container = get_hooks("createContainer"); + v.start_container = get_hooks("startContainer"); + v.poststart = get_hooks("poststart"); + v.poststop = get_hooks("poststop"); +} - if (j.contains(ptr / "process" / "user" / "additionalGids")) { - cfg.process.user.additional_gids = - j[ptr / "process" / "user" / "additionalGids"].get>(); - } +void from_json(const nlohmann::json &j, config::mount_t &v) +{ + if (auto it = j.find("source"); it != j.end()) { + v.source = it->get(); } - if (auto linux_ptr = ptr / "linux"; j.contains(linux_ptr)) { - cfg.linux = parse_linux(j, linux_ptr); + if (auto it = j.find("destination"); it != j.end()) { + v.destination = it->get(); } - if (j.contains(ptr / "hooks")) { - auto hooks = j[ptr / "hooks"]; - auto get_hooks = [&](const std::string &key) - -> std::optional> { - if (!hooks.contains(key)) { - return std::nullopt; - } + j.at("type").get_to(v.type); - std::vector result; - for (const auto &h : hooks[key]) { - linyaps_box::config::hooks_t::hook_t hook; - hook.path = h["path"].get(); - if (!hook.path.is_absolute()) { - throw std::runtime_error(key + "path must be absolute"); - } - - if (h.contains("args")) { - hook.args = h["args"].get>(); - } - - if (h.contains("env")) { - std::unordered_map env; - - for (const auto &e : h["env"].get>()) { - auto pos = e.find('='); - if (pos == std::string::npos) { - throw std::runtime_error("invalid env entry: " + e); - } - - env[e.substr(0, pos)] = e.substr(pos + 1); - } - - hook.env = std::move(env); - } - - if (h.contains("timeout")) { - hook.timeout = h["timeout"].get(); - if (hook.timeout <= 0) { - throw std::runtime_error(key + "timeout must be greater than zero"); - } - } - - result.push_back(hook); - } + if (auto it = j.find("options"); it != j.end()) { + auto options = it->get>(); + std::tie(v.flags, v.propagation_flags, v.extension_flags, v.data) = + parse_mount_options(options); + } +} - return result; - }; +void from_json(const nlohmann::json &j, config::root_t &v) +{ + j.at("path").get_to(v.path); - cfg.hooks.prestart = get_hooks("prestart"); - cfg.hooks.create_runtime = get_hooks("createRuntime"); - cfg.hooks.create_container = get_hooks("createContainer"); - cfg.hooks.start_container = get_hooks("startContainer"); - cfg.hooks.poststart = get_hooks("poststart"); - cfg.hooks.poststop = get_hooks("poststop"); + if (auto it = j.find("readonly"); it != j.end()) { + it->get_to(v.readonly); } +} - if (j.contains(ptr / "mounts")) { - std::vector mounts; - for (const auto &m : j[ptr / "mounts"]) { - linyaps_box::config::mount_t mount; - if (m.contains("source")) { - mount.source = m["source"].get(); - } - if (m.contains("destination")) { - mount.destination = m["destination"].get(); - } - mount.type = m["type"].get(); +void from_json(const nlohmann::json &j, config &v) +{ + auto semver = linyaps_box::utils::semver(j.at("ociVersion").get_ref()); + if (!linyaps_box::utils::semver(config::oci_version).is_compatible_with(semver)) { + throw std::runtime_error("unsupported OCI version: " + semver.to_string()); + } - const auto it = m.find("options"); - if (it != m.end()) { - auto options = it->get>(); - std::tie(mount.flags, mount.propagation_flags, mount.extension_flags, mount.data) = - parse_mount_options(options); - } + j.at("process").get_to(v.process); - mounts.push_back(mount); - } - cfg.mounts = mounts; + if (auto it = j.find("linux"); it != j.end()) { + v.linux = it->get(); } - auto root = ptr / "root"; - if (!j.contains(root)) { - throw std::runtime_error("root must be specified"); + if (auto it = j.find("hooks"); it != j.end()) { + v.hooks = it->get(); } - if (!j.contains(root / "path")) { - throw std::runtime_error("root.path must be specified"); + if (auto it = j.find("mounts"); it != j.end()) { + v.mounts = it->get>(); } - cfg.root.path = j[root / "path"].get(); - if (j.contains(root / "readonly")) { - cfg.root.readonly = j[root / "readonly"].get(); + if (!j.contains("root")) { + throw std::runtime_error("root must be specified"); } + j.at("root").get_to(v.root); - auto annotations = ptr / "annotations"; - if (j.contains(annotations)) { - cfg.annotations = j[annotations].get>(); + if (auto it = j.find("annotations"); it != j.end()) { + v.annotations = it->get>(); } - - return cfg; } -} // namespace +} // namespace linyaps_box -linyaps_box::config linyaps_box::config::parse(std::istream &is) +linyaps_box::config linyaps_box::config::parse(std::string_view content) { - auto j = nlohmann::json::parse(is); - return parse_1_2_0(j); + return nlohmann::json::parse(content).get(); } std::string linyaps_box::to_string(linyaps_box::config::linux_t::namespace_t::type type) noexcept diff --git a/src/linyaps_box/config.h b/src/linyaps_box/config.h index 8a78a53..7c40722 100644 --- a/src/linyaps_box/config.h +++ b/src/linyaps_box/config.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022-2025 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022-2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -31,7 +32,7 @@ struct config { static constexpr auto oci_version = "1.2.0"; - static auto parse(std::istream &is) -> config; + static auto parse(std::string_view content) -> config; struct process_t { diff --git a/src/linyaps_box/container.cpp b/src/linyaps_box/container.cpp index 27106fc..aa81709 100644 --- a/src/linyaps_box/container.cpp +++ b/src/linyaps_box/container.cpp @@ -2428,13 +2428,10 @@ linyaps_box::container::container(const status_directory &status_dir, config = bundle / config; } - std::ifstream ifs(config); - if (!ifs) { - throw std::runtime_error("Can't open config file " + config.string()); - } - LINYAPS_BOX_DEBUG() << "load config from " << config; - this->config = linyaps_box::config::parse(ifs); + const auto config_str = linyaps_box::utils::read_all(config); + + this->config = linyaps_box::config::parse(config_str); host_uid_ = ::geteuid(); host_gid_ = ::getegid(); diff --git a/src/linyaps_box/utils/file.cpp b/src/linyaps_box/utils/file.cpp index 485d5ef..503a67f 100644 --- a/src/linyaps_box/utils/file.cpp +++ b/src/linyaps_box/utils/file.cpp @@ -1,7 +1,6 @@ -// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later - #include "linyaps_box/utils/file.h" #include "linyaps_box/utils/inspect.h" @@ -10,6 +9,7 @@ #include #include +#include #ifdef LINYAPS_BOX_HAVE_OPENAT2_H #include @@ -67,6 +67,16 @@ auto syscall_openat2(int dirfd, const char *path, uint64_t flag, uint64_t mode, return linyaps_box::utils::file_descriptor{ static_cast(ret) }; } +auto read_pseudo_file(const std::filesystem::path &path) -> std::string +{ + std::ifstream ifs(path, std::ios::in | std::ios::binary); + if (!ifs) { + throw std::runtime_error("Can't open pseudo file " + path.string()); + } + + return { (std::istreambuf_iterator(ifs)), std::istreambuf_iterator() }; +} + } // namespace namespace linyaps_box::utils { @@ -146,7 +156,7 @@ auto touch(const file_descriptor &root, const std::filesystem::path &path, int f auto fstat(const file_descriptor &fd) -> struct stat { - struct stat statbuf{}; + struct stat statbuf{ }; auto ret = ::fstat(fd.get(), &statbuf); if (ret == -1) { throw std::system_error(errno, std::system_category(), "fstat"); @@ -159,7 +169,7 @@ auto fstat(const file_descriptor &fd) -> struct stat auto fstatat(const file_descriptor &fd, const std::filesystem::path &path, int flag) -> struct stat { - struct stat statbuf{}; + struct stat statbuf{ }; auto ret = ::fstatat(fd.get(), path.c_str(), &statbuf, flag); if (ret == -1) { throw std::system_error(errno, std::system_category(), "fstatat"); @@ -186,7 +196,7 @@ auto lstatat(const file_descriptor &fd, const std::filesystem::path &path) -> st auto lstat(const std::filesystem::path &path) -> struct stat { - struct stat statbuf{}; + struct stat statbuf{ }; auto ret = ::lstat(path.c_str(), &statbuf); if (ret == -1) { throw std::system_error(errno, @@ -201,7 +211,7 @@ auto lstat(const std::filesystem::path &path) -> struct stat auto statfs(const file_descriptor &fd) -> struct statfs { - struct statfs statbuf{}; + struct statfs statbuf{ }; auto ret = ::statfs(fd.proc_path().c_str(), &statbuf); if (ret == -1) { throw std::system_error(errno, std::system_category(), "statfs"); @@ -318,4 +328,28 @@ auto to_string(std::filesystem::file_type type) noexcept -> std::string_view __builtin_unreachable(); } +auto read_all(const std::filesystem::path &path) -> std::string +{ + auto fd = open(path, O_RDONLY | O_CLOEXEC); + auto stat = fstat(fd); + if (stat.st_size == 0) { + return read_pseudo_file(path); + } + return read_all(fd, stat.st_size); +} + +auto read_all(const file_descriptor &fd, std::size_t size) -> std::string +{ + std::string content; + content.resize(size); + std::size_t bytes_read{ 0 }; + const span buffer(reinterpret_cast(content.data()), size); + auto status = fd.read_span(buffer, bytes_read); + if (status != utils::file_descriptor::IOStatus::Success) { + throw std::runtime_error("Failed to read file: " + fd.current_path().string()); + } + + return content; +} + } // namespace linyaps_box::utils diff --git a/src/linyaps_box/utils/file.h b/src/linyaps_box/utils/file.h index 4f16afc..afa457c 100644 --- a/src/linyaps_box/utils/file.h +++ b/src/linyaps_box/utils/file.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -61,4 +61,8 @@ auto is_type(mode_t mode, mode_t type) noexcept -> bool; auto to_string(std::filesystem::file_type type) noexcept -> std::string_view; +auto read_all(const std::filesystem::path &path) -> std::string; + +auto read_all(const file_descriptor &fd, std::size_t size) -> std::string; + } // namespace linyaps_box::utils