#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 = "Failed to read ICF file"; 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("Parse ICF failed: {}", parse_err); return result; } // 筛选Active状态的APP条目,按part_index排序 auto app_entries = get_active_apps(*icf_file); if (app_entries.empty()) { result.error = "No active APP entries found in ICF"; 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("Part index not consecutive: {} -> {}", prev->part_index, cur->part_index); return result; } if (cur->base_version != prev->version) { result.error = std::format("Base version mismatch for part {}: {} != {}", cur->part_index, cur->base_version, prev->version); return result; } } // APP/OPT文件所在目录:优先使用image_dir,否则使用exe所在目录 std::wstring dir = cfg.image_dir.empty() ? utils::get_exe_dir() : cfg.image_dir; // 构造密钥override(config.ini [KEYS] section,空则用内置密钥表) fscrypt::KeyOverride key_override; key_override.app_key_hex = cfg.app_key; key_override.app_iv_hex = cfg.app_iv; key_override.opt_key_hex = cfg.opt_key; key_override.opt_iv_hex = 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("Mount part {}: {}", entry->part_index, entry->filename); auto mount_res = fscrypt::mount_container(app_path, tag, &key_override); mount_entry.success = mount_res.success; mount_entry.error = mount_res.error; if (!mount_res.success) { LOG_ERROR("Mount part {} failed: {}", 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 exe_dir = utils::get_exe_dir(); std::wstring overlay_vhd = std::format(L"{}\\overlay.vhd", exe_dir); 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("Creating overlay.vhd (parent: 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 create failed: {}", ov_err); all_ok = false; } } else { LOG_INFO("Reusing existing 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 failed: {}", 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("Assign drive {} failed: Error = {}", drive, GetLastError()); all_ok = false; } else { LOG_INFO("Overlay mounted to {}:", 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 copy failed: {}", cp_err); all_ok = false; } else { LOG_INFO("Copied {} files", copied); } } else { LOG_INFO("OVERLAY_DIR not found, skip copy"); } } else { LOG_INFO("OVERLAY_DIR empty, skip copy"); } } } } } } // 挂载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("Mount OPT {}: {}", entry->version, entry->filename); auto mount_res = fscrypt::mount_container(opt_path, opt_tag, &key_override); mount_entry.success = mount_res.success; mount_entry.error = mount_res.error; if (!mount_res.success) { LOG_ERROR("Mount OPT {} failed: {}", entry->version, mount_res.error); } 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("Link APP {} failed", 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("Link OPT {} failed", opt.version); } } } result.success = all_ok; if (!all_ok) { result.error = "Some APPs failed to mount"; } 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 = "Failed to read ICF file"; return result; } std::string parse_err; auto icf_file = icf::parse_file(icf_data, &parse_err); if (!icf_file) { result.error = std::format("Parse ICF failed: {}", 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 exe_dir = utils::get_exe_dir(); std::wstring overlay_vhd = std::format(L"{}\\overlay.vhd", exe_dir); { 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("Removed drive letter {}", drive); } WIN32_FILE_ATTRIBUTE_DATA fa; if (GetFileAttributesExW(overlay_vhd.c_str(), GetFileExInfoStandard, &fa)) { LOG_INFO("Detaching overlay.vhd..."); std::string vhd_err; if (!vhd_mount::detach_vhd(overlay_vhd, &vhd_err)) { LOG_ERROR("Overlay detach failed: {}", vhd_err); all_ok = false; } else { LOG_INFO("Detached overlay.vhd"); } } else { LOG_INFO("overlay.vhd not found, skip detach"); } } // 4) 倒序卸载APP容器 for (auto it = app_entries.rbegin(); it != app_entries.rend(); ++it) { auto *entry = *it; 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("Unmount part {}: {}", entry->part_index, entry->filename); bool ok = fscrypt::unmount_container(tag); mount_entry.success = ok; if (!ok) { mount_entry.error = "Unmount failed"; LOG_ERROR("Unmount part {} failed", 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("Unmount OPT {}: {}", entry->version, entry->filename); bool ok = fscrypt::unmount_container(opt_tag); mount_entry.success = ok; if (!ok) { mount_entry.error = "Unmount failed"; LOG_ERROR("Unmount OPT {} failed", entry->version); all_ok = false; } result.opts.push_back(mount_entry); } result.success = all_ok; if (!all_ok) { result.error = "Some containers failed to unmount"; } return result; } }