fix: CLI 写操作加原子性保护 — 保存前重新读取注册表对比

所有修改命令(add/remove/edit/move/clean/import)在保存前重新读取
注册表,与操作前加载的值对比,不一致则报错退出,防止覆盖其他进程的修改。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 23:56:50 +08:00
parent 1320aa57a8
commit 9b5b57a3ac
+33 -18
View File
@@ -117,17 +117,28 @@ fn ensure_single_target(system: bool, user: bool) -> &'static str {
type SaveFn = fn(Vec<String>) -> Result<(), String>; type SaveFn = fn(Vec<String>) -> Result<(), String>;
fn verify_and_save(target: &str, original: &[String], new_list: Vec<String>) {
let reload = if target == "system" {
core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e))
} else {
core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e))
};
if reload != original {
exit_err("注册表已被其他进程修改,请重新执行操作");
}
let save: SaveFn = if target == "system" { core::registry::save_system_paths } else { core::registry::save_user_paths };
save(new_list).unwrap_or_else(|e| exit_err(&e));
}
fn load_and_save(system: bool, f: impl FnOnce(Vec<String>) -> Vec<String>) { fn load_and_save(system: bool, f: impl FnOnce(Vec<String>) -> Vec<String>) {
let target = ensure_single_target(system, false); let target = ensure_single_target(system, false);
let (list, save): (Vec<String>, SaveFn) = if target == "system" { let list = if target == "system" {
(core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e)), core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e))
core::registry::save_system_paths)
} else { } else {
(core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e)), core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e))
core::registry::save_user_paths)
}; };
let new_list = f(list); let new_list = f(list.clone());
save(new_list).unwrap_or_else(|e| exit_err(&e)); verify_and_save(target, &list, new_list);
} }
// ── 命令实现 ── // ── 命令实现 ──
@@ -174,10 +185,10 @@ fn cmd_remove(index: usize, system: bool) {
} else { } else {
core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e)) core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e))
}; };
let original = list.clone();
if index >= list.len() { exit_err(&format!("索引 {index} 超出范围 (共 {} 条)", list.len())); } if index >= list.len() { exit_err(&format!("索引 {index} 超出范围 (共 {} 条)", list.len())); }
let removed = list.remove(index); let removed = list.remove(index);
let save: SaveFn = if target == "system" { core::registry::save_system_paths } else { core::registry::save_user_paths }; verify_and_save(target, &original, list);
save(list).unwrap_or_else(|e| exit_err(&e));
println!("已删除: {removed}"); println!("已删除: {removed}");
core::system::broadcast_env_change(); core::system::broadcast_env_change();
} }
@@ -190,9 +201,9 @@ fn cmd_edit(index: usize, new_path: String, system: bool) {
core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e)) core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e))
}; };
if index >= list.len() { exit_err(&format!("索引 {index} 超出范围 (共 {} 条)", list.len())); } if index >= list.len() { exit_err(&format!("索引 {index} 超出范围 (共 {} 条)", list.len())); }
let original = list.clone();
let old = std::mem::replace(&mut list[index], new_path.clone()); let old = std::mem::replace(&mut list[index], new_path.clone());
let save: SaveFn = if target == "system" { core::registry::save_system_paths } else { core::registry::save_user_paths }; verify_and_save(target, &original, list);
save(list).unwrap_or_else(|e| exit_err(&e));
println!("已编辑: {old}{new_path}"); println!("已编辑: {old}{new_path}");
core::system::broadcast_env_change(); core::system::broadcast_env_change();
} }
@@ -222,7 +233,7 @@ fn cmd_clean(system: bool, user: bool, dry_run: bool, json_out: bool) {
} else { } else {
core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e)) core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e))
}; };
let (kept, removed) = core::registry::clean_paths(list); let (kept, removed) = core::registry::clean_paths(list.clone());
if json_out { if json_out {
println!("{}", json!({ "kept": kept, "removed": removed, "kept_count": kept.len(), "removed_count": removed.len() }).to_string()); println!("{}", json!({ "kept": kept, "removed": removed, "kept_count": kept.len(), "removed_count": removed.len() }).to_string());
@@ -233,8 +244,7 @@ fn cmd_clean(system: bool, user: bool, dry_run: bool, json_out: bool) {
for k in &kept { println!("{}", k); } for k in &kept { println!("{}", k); }
} else { } else {
let kept_count = kept.len(); let kept_count = kept.len();
let save: SaveFn = if target == "system" { core::registry::save_system_paths } else { core::registry::save_user_paths }; verify_and_save(target, &list, kept);
save(kept).unwrap_or_else(|e| exit_err(&e));
println!("清理完成:移除 {} 条,保留 {}", removed.len(), kept_count); println!("清理完成:移除 {} 条,保留 {}", removed.len(), kept_count);
core::system::broadcast_env_change(); core::system::broadcast_env_change();
if !removed.is_empty() { if !removed.is_empty() {
@@ -271,19 +281,24 @@ fn cmd_import(file: String, target: String) {
let (sys, usr) = core::fs::import_paths(&file, &content).unwrap_or_else(|e| exit_err(&e)); let (sys, usr) = core::fs::import_paths(&file, &content).unwrap_or_else(|e| exit_err(&e));
match target.as_str() { match target.as_str() {
"system" => { "system" => {
core::registry::save_system_paths(sys).unwrap_or_else(|e| exit_err(&e)); let orig = core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e));
verify_and_save("system", &orig, sys);
println!("已导入到系统 PATH"); println!("已导入到系统 PATH");
} }
"user" => { "user" => {
core::registry::save_user_paths(usr).unwrap_or_else(|e| exit_err(&e)); let orig = core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e));
verify_and_save("user", &orig, usr);
println!("已导入到用户 PATH"); println!("已导入到用户 PATH");
} }
_ => { _ => {
core::registry::save_system_paths(sys).unwrap_or_else(|e| exit_err(&e)); let orig_sys = core::registry::load_system_paths().unwrap_or_else(|e| exit_err(&e));
core::registry::save_user_paths(usr).unwrap_or_else(|e| exit_err(&e)); let orig_usr = core::registry::load_user_paths().unwrap_or_else(|e| exit_err(&e));
verify_and_save("system", &orig_sys, sys);
verify_and_save("user", &orig_usr, usr);
println!("已导入到系统 + 用户 PATH"); println!("已导入到系统 + 用户 PATH");
} }
} }
core::system::broadcast_env_change();
} }
fn cmd_export(format: String, output: Option<String>) { fn cmd_export(format: String, output: Option<String>) {