Plot style and plotter files save atomically through write_atomic (#1928 follow-up)

This commit is contained in:
MrBeeboh
2026-10-22 20:22:09 -07:00
parent a08fd7c1d1
commit 6ed822ec30
3 changed files with 30 additions and 6 deletions
+2 -3
View File
@@ -332,9 +332,8 @@ fn run_stylesmanager(s: &mut Session, p: &Value) -> Result<Value> {
Some(_path) => {
#[cfg(not(target_arch = "wasm32"))]
{
let tmp = format!("{_path}.cadcraft-tmp");
std::fs::write(&tmp, text.as_bytes()).map_err(|e| bad(cmd, format!("{_path}: {e}")))?;
std::fs::rename(&tmp, _path).map_err(|e| bad(cmd, format!("{_path}: {e}")))?;
crate::atomic_write::write_atomic(std::path::Path::new(_path), text.as_bytes())
.map_err(|e| bad(cmd, format!("{_path}: {e}")))?;
Ok(json!({ "name": name, "path": _path, "bytes": text.len() }))
}
#[cfg(target_arch = "wasm32")]
+1 -3
View File
@@ -110,9 +110,7 @@ impl PlotterStore {
write: |n, t| {
let p = file(n)?;
std::fs::create_dir_all(dir()?).map_err(|e| e.to_string())?;
let tmp = p.with_extension("json.cadcraft-tmp");
std::fs::write(&tmp, t).map_err(|e| format!("{n}: {e}"))?;
std::fs::rename(&tmp, &p).map_err(|e| format!("{n}: {e}"))
crate::atomic_write::write_atomic(&p, t.as_bytes()).map_err(|e| format!("{n}: {e}"))
},
delete: |n| std::fs::remove_file(file(n)?).map_err(|e| format!("{n}: {e}")),
}
+27
View File
@@ -104,3 +104,30 @@ fn stylesmanager_creates_edits_saves_and_loads_tables() {
// Undo takes a table edit back.
s.execute("undo", &json!({})).unwrap();
}
/// Issue #1928: saving a plot style table to a file goes through the atomic writer: a symlink
/// planted at the old fixed temporary name is left alone, and the file keeps its permissions.
#[cfg(unix)]
#[test]
fn plot_style_save_is_atomic_and_keeps_permissions() {
use std::os::unix::fs::PermissionsExt;
let dir = std::env::temp_dir().join(format!("cadcraft-pstyle-atomic-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("office.stb.json");
std::fs::write(&path, "old").unwrap();
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600)).unwrap();
let victim = dir.join("victim");
std::fs::write(&victim, "keep").unwrap();
std::os::unix::fs::symlink(&victim, dir.join("office.stb.json.cadcraft-tmp")).unwrap();
let mut s = Session::new();
s.execute("stylesmanager", &json!({"action": "new", "name": "office.stb"})).unwrap();
let p = path.to_string_lossy().to_string();
s.execute("stylesmanager", &json!({"action": "save", "name": "office.stb", "path": p})).unwrap();
assert_ne!(std::fs::read_to_string(&path).unwrap(), "old");
assert_eq!(std::fs::read_to_string(&victim).unwrap(), "keep");
assert_eq!(std::fs::metadata(&path).unwrap().permissions().mode() & 0o777, 0o600);
let _ = std::fs::remove_dir_all(&dir);
}