From 6553466ed626836f24142fe7f9d496484d9fa217 Mon Sep 17 00:00:00 2001 From: Tunglies <77394545+Tunglies@users.noreply.github.com> Date: Wed, 28 Oct 2026 09:17:15 +0800 Subject: [PATCH] fix(backup): serialize restore with config write lock Acquire the configuration write lock at the entry of finalize_restored_verge_config so the clash, profiles and verge draft applies all happen under it, instead of only the verge layer. A concurrent patch (patch_clash / patch_verge via try_lock_config_write) could otherwise stage a draft that the unlocked restore applies overwrite, or interleave its persistence with the restore. Also drop the pointless clones in the clash and profiles edit_draft closures; edit_draft takes FnOnce so the restored values can be moved in. The lock is released before patch_verge, which re-acquires it via try_lock_config_write. --- Changelog.md | 4 ++++ src-tauri/src/feat/backup.rs | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index ddedcb2f4..b6611ae59 100644 --- a/Changelog.md +++ b/Changelog.md @@ -24,14 +24,18 @@
🐞 修复问题 +- 修复恢复备份的同时修改配置,可能导致配置丢失或错乱的问题 +
✨ 新增功能 +
🚀 优化改进 +
diff --git a/src-tauri/src/feat/backup.rs b/src-tauri/src/feat/backup.rs index 449fba642..688083332 100644 --- a/src-tauri/src/feat/backup.rs +++ b/src-tauri/src/feat/backup.rs @@ -33,6 +33,9 @@ async fn finalize_restored_verge_config( webdav_username: Option, webdav_password: Option, ) -> Result<()> { + // The lock must cover every restored layer; an unlocked apply races a concurrently staged draft. + let config_write = Config::lock_config_write().await; + // A broken restored `verge.yaml` is a restore failure, not a reason to load defaults. let mut restored = help::read_yaml::(&verge_path()?).await?; restored.webdav_url = webdav_url; @@ -55,14 +58,10 @@ async fn finalize_restored_verge_config( profiles_draft.apply(); let verge_draft = Config::verge().await; - { - // Hold the write lock so a concurrent patch cannot stage a draft the proxy write reads. - let _config_write = Config::lock_config_write().await; - verge_draft.edit_draft(|d| { - *d = restored.clone(); - }); - verge_draft.apply(); - } + verge_draft.edit_draft(|d| { + *d = restored.clone(); + }); + verge_draft.apply(); // Turn it off here; a failing core side effect below would otherwise skip this step. if !restored.enable_system_proxy.unwrap_or_default() { @@ -80,6 +79,9 @@ async fn finalize_restored_verge_config( } } + // `patch_verge` re-acquires the configuration write lock. + drop(config_write); + // Run configuration side effects without rewriting the already-restored file. if let Err(err) = super::patch_verge(&restored, true).await { logging!(error, Type::Backup, "Failed to apply restored verge config: {err:#}");