修复策划V2不可变文件失败残留

失败时清理已创建的空文件或截断文件

保留已有文件的幂等与内容冲突校验
This commit is contained in:
2026-09-05 12:17:04 +00:00
parent 267c085b57
commit b84c9d4e45
@@ -839,13 +839,24 @@ fn create_v2_immutable_file(root: &Path, relative: &str, bytes: &[u8]) -> Result
.open(&path)
{
Ok(mut file) => {
harden_new_game_creator_private_path(&path, false, "Planning V2 文件")?;
file.write_all(bytes)
.and_then(|_| file.sync_data())
.map_err(|error| {
format!("写入 Planning V2 文件失败:{}: {error}", path.display())
})?;
Ok(())
// `create_new` has already made the final path visible. Every
// subsequent step must therefore be transactional from the
// caller's perspective: a failed ACL repair, partial write, or
// failed sync must not leave an empty/truncated immutable file
// that would permanently block a retry.
let result = (|| {
harden_new_game_creator_private_path(&path, false, "Planning V2 文件")?;
file.write_all(bytes)
.and_then(|_| file.sync_data())
.map_err(|error| {
format!("写入 Planning V2 文件失败:{}: {error}", path.display())
})?;
Ok(())
})();
if result.is_err() {
let _ = fs::remove_file(&path);
}
result
}
Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {
prepare_game_creator_private_path_for_read(&path, false, "Planning V2 文件")?;