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.
This commit is contained in:
Tunglies
2026-10-28 09:17:15 +08:00
parent acef575935
commit 34607dce55
2 changed files with 11 additions and 8 deletions
+1
View File
@@ -10,6 +10,7 @@
- 修复 代理 页展开分组后不显示最新测速结果,需切换节点才刷新
- 修复 代理 在全局模式下无法展开节点组
- 修复 自定义 CSS 无法修改编辑器字体和字号的问题
- 修复恢复备份的同时修改配置,可能导致配置丢失或错乱的问题
**🐧 Linux**
+10 -8
View File
@@ -33,6 +33,9 @@ async fn finalize_restored_verge_config(
webdav_username: Option<String>,
webdav_password: Option<String>,
) -> 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::<IVerge>(&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 lock through `try_lock_config_write`.
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:#}");