#define LOG_MODULE "ICF" #include "icf_mount.h" #include "icf_parser.h" #include "fscrypt.h" #include "vhd_mount.h" #include "utils/files.h" #include "utils/strings.h" #include "utils/log.h" #include #include #include #include namespace icf_mount { // 前向声明 static std::vector get_active_apps(const icf::IcfFile &icf_file); static std::vector get_active_opts(const icf::IcfFile &icf_file); // 解析ICF文件并挂载所有Active状态的APP和OPT容器 // 流程:挂载APP → 创建/挂载overlay → 挂载OPT → 创建LINK IcfMountResult mount_icf_apps(const Config &cfg) { IcfMountResult result{}; result.success = false; // 读取ICF文件 auto icf_data = utils::read_file(cfg.icf_path); if (icf_data.empty()) { result.error = "读取 ICF 文件失败"; return result; } // 解密并解析ICF(parse_file内部会处理解密) std::string parse_err; auto icf_file = icf::parse_file(icf_data, &parse_err); if (!icf_file) { result.error = std::format("解析 ICF 失败:{}", parse_err); return result; } // 筛选Active状态的APP条目,按part_index排序 auto app_entries = get_active_apps(*icf_file); if (app_entries.empty()) { result.error = "ICF 中未找到活跃的 APP 条目"; return result; } // 验证base version依赖关系 // part 0 没有base version // part n (n>0) 的 base_version 应该等于 part n-1 的 version for (size_t i = 1; i < app_entries.size(); i++) { auto *cur = app_entries[i]; auto *prev = app_entries[i - 1]; if (cur->part_index != prev->part_index + 1) { result.error = std::format("分区序号不连续:{} -> {}", prev->part_index, cur->part_index); return result; } if (cur->base_version != prev->version) { result.error = std::format("分区 {} 基础版本不匹配:{} != {}", cur->part_index, cur->base_version, prev->version); return result; } } // APP/OPT文件所在目录:优先使用image_dir,否则使用当前目录 std::wstring dir = cfg.image_dir.empty() ? utils::get_cwd() : cfg.image_dir; // 设置全局密钥覆盖(config.ini [KEYS] section,空则用内置密钥表) fscrypt::set_key(cfg.app_key, cfg.app_iv, cfg.opt_key, cfg.opt_iv); // 依次挂载每个APP容器 bool all_ok = true; for (auto entry: app_entries) { MountEntry mount_entry{}; mount_entry.filename = entry->filename; mount_entry.version = entry->version; mount_entry.part_index = entry->part_index; mount_entry.tag = std::format("APP_{}", entry->part_index); mount_entry.vhd_success = false; std::wstring app_path = std::format(L"{}\\{}", dir, utils::to_wstring(entry->filename)); std::wstring tag = std::format(L"APP_{}", entry->part_index); LOG_INFO("挂载分区 {}:{}", entry->part_index, entry->filename); auto mount_res = fscrypt::mount_container(app_path, tag); mount_entry.success = mount_res.success; mount_entry.error = mount_res.error; if (!mount_res.success) { LOG_ERROR("挂载分区 {} 失败:{}", entry->part_index, mount_res.error); all_ok = false; } result.apps.push_back(mount_entry); } // 所有APP容器挂载完成后,处理overlay写层: // overlay.vhd不存在 → 创建差分盘 + 拷贝overlay目录文件 // overlay.vhd已存在 → 直接挂载(数据已在里面,无需重新拷贝) if (all_ok && !app_entries.empty()) { auto *last = app_entries.back(); std::wstring overlay_vhd = std::format(L"{}\\overlay.vhd", utils::get_cwd()); std::wstring last_tag = std::format(L"APP_{}", last->part_index); std::wstring parent_for_create = std::format(L"\\\\?\\FscryptDisk_{}\\internal_{}.vhd", last_tag, last->part_index); bool vhd_exists = GetFileAttributesW(overlay_vhd.c_str()) != INVALID_FILE_ATTRIBUTES; if (!vhd_exists) { // 创建overlay.vhd差分盘 LOG_INFO("创建 overlay.vhd(父盘:internal_{}.vhd)", last->part_index); std::string ov_err; if (!vhd_mount::create_diff_vhd(overlay_vhd, parent_for_create, &ov_err)) { LOG_ERROR("创建 overlay 失败:{}", ov_err); all_ok = false; } } else { LOG_INFO("复用已有 overlay.vhd"); // 先尝试detach,处理上次未正常卸载的情况 vhd_mount::detach_vhd(overlay_vhd, nullptr); } if (all_ok) { // 挂载overlay.vhd vhd_mount::MountedVhdInfo vhd_info{}; std::string attach_err; if (!vhd_mount::attach_vhd(overlay_vhd, vhd_info, &attach_err)) { LOG_ERROR("附加 overlay 失败:{}", attach_err); all_ok = false; } else { result.apps.back().vhd_success = true; // 分配盘符到overlay卷 char drive = static_cast(cfg.mount_letter); std::wstring mount_point = std::format(L"{}:\\", cfg.mount_letter); DeleteVolumeMountPointW(mount_point.c_str()); // 清理可能残留的旧挂载点 if (!SetVolumeMountPointW(mount_point.c_str(), vhd_info.volume_guid.c_str())) { LOG_ERROR("分配盘符 {} 失败:{}", drive, utils::last_error_str()); all_ok = false; } else { LOG_INFO("overlay 已挂载到 {}:", drive); // 仅新建overlay.vhd时才拷贝overlay目录文件 // OVERLAY_DIR为空或不存在时跳过拷贝 if (!vhd_exists) { if (!cfg.overlay_dir.empty()) { DWORD attr = GetFileAttributesW(cfg.overlay_dir.c_str()); bool dir_exists = attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY; if (dir_exists) { std::string cp_err; size_t copied = 0; if (!utils::copy_dir_overwrite(cfg.overlay_dir, mount_point, &cp_err, &copied)) { LOG_ERROR("overlay 文件拷贝失败:{}", cp_err); all_ok = false; } else { LOG_INFO("已拷贝 {} 个文件", copied); } } else { LOG_INFO("OVERLAY_DIR 目录不存在,跳过拷贝"); } } else { LOG_INFO("OVERLAY_DIR 为空,跳过拷贝"); } } } } } } // 挂载OPT容器(独立于APP/overlay,不受其结果影响) auto opt_entries = get_active_opts(*icf_file); for (auto entry: opt_entries) { MountEntry mount_entry{}; mount_entry.filename = entry->filename; mount_entry.version = entry->version; mount_entry.part_index = entry->part_index; mount_entry.tag = std::format("OPT_{}", entry->version); mount_entry.vhd_success = false; std::wstring opt_path = std::format(L"{}\\{}", dir, utils::to_wstring(entry->filename)); std::wstring opt_tag = std::format(L"OPT_{}", utils::to_wstring(entry->version)); LOG_INFO("挂载 OPT {}:{}", entry->version, entry->filename); auto mount_res = fscrypt::mount_container(opt_path, opt_tag); mount_entry.success = mount_res.success; mount_entry.error = mount_res.error; if (!mount_res.success) { LOG_ERROR("挂载 OPT {} 失败:{}", entry->version, mount_res.error); all_ok = false; } result.opts.push_back(mount_entry); } // 创建APP LINK(仅对挂载成功的APP) if (!cfg.app_link.empty()) { for (const auto &app: result.apps) { if (!app.success) continue; std::wstring tag = std::format(L"APP_{}", app.part_index); std::wstring link_path = std::format(L"{}\\{}", cfg.app_link, app.part_index); if (!fscrypt::link_container(tag, link_path)) { LOG_ERROR("链接 APP {} 失败", app.part_index); } } } // 创建OPT LINK(仅对挂载成功的OPT) if (!cfg.opt_link.empty()) { for (const auto &opt: result.opts) { if (!opt.success) continue; std::wstring tag = std::format(L"OPT_{}", utils::to_wstring(opt.version)); std::wstring link_path = std::format(L"{}\\{}", cfg.opt_link, utils::to_wstring(opt.version)); if (!fscrypt::link_container(tag, link_path)) { LOG_ERROR("链接 OPT {} 失败", opt.version); } } } result.success = all_ok; if (!all_ok) { result.error = "部分容器挂载失败,详见日志"; } return result; } // 从ICF中获取所有Active状态的APP条目,同一part_index只保留timestamp最新的,按part_index升序 static std::vector get_active_apps(const icf::IcfFile &icf_file) { // 按 part_index 分组,每组保留 timestamp 最大的条目(timestamp格式为 YYYYMMDDhhmmss,定长数字串可直接比较) std::map latest_by_part; for (const auto &entry: icf_file.entries) { if (entry.type == icf::EntryType::App && static_cast(entry.status) == 0x0102) { auto it = latest_by_part.find(entry.part_index); if (it == latest_by_part.end() || entry.timestamp > it->second->timestamp) { latest_by_part[entry.part_index] = &entry; } } } // map 按 key 升序,直接收集 values 即可 std::vector app_entries; app_entries.reserve(latest_by_part.size()); for (const auto *entry: latest_by_part | std::views::values) { app_entries.push_back(entry); } return app_entries; } // 从ICF中获取所有Active状态的OPT条目,同一version只保留timestamp最新的 static std::vector get_active_opts(const icf::IcfFile &icf_file) { // OPT的version字段为4字符名称(如A001),按此分组去重 std::map latest_by_name; for (const auto &entry: icf_file.entries) { if (entry.type == icf::EntryType::Opt && static_cast(entry.status) == 0x0102) { auto it = latest_by_name.find(entry.version); if (it == latest_by_name.end() || entry.timestamp > it->second->timestamp) { latest_by_name[entry.version] = &entry; } } } std::vector opt_entries; opt_entries.reserve(latest_by_name.size()); for (const auto *entry: latest_by_name | std::views::values) { opt_entries.push_back(entry); } return opt_entries; } // 解析ICF文件并卸载所有容器 // 流程:移除LINK → 卸载overlay → 倒序卸载APP → 卸载OPT IcfMountResult unmount_icf_apps(const Config &cfg) { IcfMountResult result{}; result.success = false; // 读取并解析ICF auto icf_data = utils::read_file(cfg.icf_path); if (icf_data.empty()) { result.error = "读取 ICF 文件失败"; return result; } std::string parse_err; auto icf_file = icf::parse_file(icf_data, &parse_err); if (!icf_file) { result.error = std::format("解析 ICF 失败:{}", parse_err); return result; } auto app_entries = get_active_apps(*icf_file); auto opt_entries = get_active_opts(*icf_file); bool all_ok = true; // 1) 移除APP LINK if (!cfg.app_link.empty()) { for (auto *entry: app_entries) { std::wstring link_path = std::format(L"{}\\{}", cfg.app_link, entry->part_index); fscrypt::unlink_container(link_path); } } // 2) 移除OPT LINK if (!cfg.opt_link.empty()) { for (auto *entry: opt_entries) { std::wstring link_path = std::format(L"{}\\{}", cfg.opt_link, utils::to_wstring(entry->version)); fscrypt::unlink_container(link_path); } } // 3) overlay.vhd:移除盘符 + detach(保留文件供下次沿用) std::wstring overlay_vhd = std::format(L"{}\\overlay.vhd", utils::get_cwd()); { char drive = static_cast(cfg.mount_letter); std::wstring mount_point = std::format(L"{}:\\", cfg.mount_letter); if (DeleteVolumeMountPointW(mount_point.c_str())) { LOG_INFO("已移除盘符 {}", drive); } WIN32_FILE_ATTRIBUTE_DATA fa; if (GetFileAttributesExW(overlay_vhd.c_str(), GetFileExInfoStandard, &fa)) { LOG_INFO("正在分离 overlay.vhd..."); std::string vhd_err; if (!vhd_mount::detach_vhd(overlay_vhd, &vhd_err)) { LOG_ERROR("分离 overlay 失败:{}", vhd_err); all_ok = false; } else { LOG_INFO("已分离 overlay.vhd"); } } else { LOG_INFO("overlay.vhd 不存在,跳过分离"); } } // 4) 倒序卸载APP容器 for (auto entry : std::views::reverse(app_entries)) { MountEntry mount_entry{}; mount_entry.filename = entry->filename; mount_entry.version = entry->version; mount_entry.part_index = entry->part_index; mount_entry.tag = std::format("APP_{}", entry->part_index); mount_entry.vhd_success = false; std::wstring tag = std::format(L"APP_{}", entry->part_index); LOG_INFO("卸载分区 {}:{}", entry->part_index, entry->filename); bool ok = fscrypt::unmount_container(tag); mount_entry.success = ok; if (!ok) { mount_entry.error = "卸载失败"; LOG_ERROR("卸载分区 {} 失败", entry->part_index); all_ok = false; } result.apps.push_back(mount_entry); } // 5) 卸载OPT容器 for (auto *entry: opt_entries) { MountEntry mount_entry{}; mount_entry.filename = entry->filename; mount_entry.version = entry->version; mount_entry.part_index = entry->part_index; mount_entry.tag = std::format("OPT_{}", entry->version); mount_entry.vhd_success = false; std::wstring opt_tag = std::format(L"OPT_{}", utils::to_wstring(entry->version)); LOG_INFO("卸载 OPT {}:{}", entry->version, entry->filename); bool ok = fscrypt::unmount_container(opt_tag); mount_entry.success = ok; if (!ok) { mount_entry.error = "卸载失败"; LOG_ERROR("卸载 OPT {} 失败", entry->version); all_ok = false; } result.opts.push_back(mount_entry); } result.success = all_ok; if (!all_ok) { result.error = "部分容器卸载失败"; } return result; } }