mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 10:56:51 -06:00
Autosave: a new autosave folder is owner-only on Unix (#1868)
Autosave files are unsaved drawings. The autosave folder was made with create_dir_all, by the first write or for the lock file, so on Unix it got the default mode and other accounts on the machine could read the files. A folder the autosaver creates is now 0o700. A folder that already exists keeps its mode, since SAVEFILEPATH can be any folder the user picked. The same change as storytold/pdfcraft#3320 and storytold/vectorcraft#3406.
This commit is contained in:
@@ -334,10 +334,22 @@ mod native {
|
||||
|
||||
type WriteHook = fn(&Drawing, &str) -> std::result::Result<Vec<u8>, String>;
|
||||
|
||||
/// Create the autosave folder `dir` and any missing parents. On Unix the folders made here are
|
||||
/// owner-only (`0o700`): autosave files are unsaved drawings, and with the default mode other
|
||||
/// accounts on the machine could read them. A folder that already exists keeps its mode, since
|
||||
/// SAVEFILEPATH can be any folder the user picked.
|
||||
fn create_folder(dir: &Path) -> std::io::Result<()> {
|
||||
let mut builder = std::fs::DirBuilder::new();
|
||||
builder.recursive(true);
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::DirBuilderExt::mode(&mut builder, 0o700);
|
||||
builder.create(dir)
|
||||
}
|
||||
|
||||
fn write_file(write: WriteHook, doc: &Drawing, path: &str) -> std::result::Result<(), String> {
|
||||
let bytes = write(doc, "autosave.dxf")?;
|
||||
if let Some(dir) = Path::new(path).parent() {
|
||||
std::fs::create_dir_all(dir).map_err(|e| e.to_string())?;
|
||||
create_folder(dir).map_err(|e| e.to_string())?;
|
||||
}
|
||||
// Create a fresh temporary file exclusively and rename it into place, so a planted
|
||||
// `<path>.tmp` symlink in a shared autosave directory cannot redirect the write (CWE-59).
|
||||
@@ -434,7 +446,7 @@ mod native {
|
||||
}
|
||||
self.last_beat = Some(now);
|
||||
let dir = Path::new(dir);
|
||||
let r = std::fs::create_dir_all(dir).and_then(|()| std::fs::write(lock_path(dir, &self.instance), std::process::id().to_string()));
|
||||
let r = create_folder(dir).and_then(|()| std::fs::write(lock_path(dir, &self.instance), std::process::id().to_string()));
|
||||
if let Err(e) = r {
|
||||
log::warn!("autosave: lock file in {}: {e}", dir.display());
|
||||
}
|
||||
|
||||
@@ -211,6 +211,40 @@ fn autosave_files_are_written_in_the_background_and_removed_on_exit() {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// Autosave files are unsaved drawings: on Unix an autosave folder that doesn't exist yet is made
|
||||
/// owner-only, and one that already exists (SAVEFILEPATH can be any folder) keeps its mode.
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn a_new_autosave_folder_is_owner_only() {
|
||||
use std::os::unix::fs::PermissionsExt as _;
|
||||
let mode = |p: &std::path::Path| std::fs::metadata(p).unwrap().permissions().mode() & 0o777;
|
||||
hooks();
|
||||
let base = temp_dir("modes");
|
||||
let picked = base.join("picked");
|
||||
std::fs::create_dir_all(&picked).unwrap();
|
||||
std::fs::set_permissions(&picked, std::fs::Permissions::from_mode(0o755)).unwrap();
|
||||
for (dir, expected) in [(base.join("new").join("autosave"), 0o700), (picked, 0o755)] {
|
||||
let mut s = Session::new();
|
||||
draw(&mut s);
|
||||
let mut a = Autosaver::new(autosave::instance_id());
|
||||
a.plan(&s, 0.0, 1, &dir.to_string_lossy());
|
||||
let jobs = a.plan(&s, 60.0, 1, &dir.to_string_lossy());
|
||||
let path = writes(&jobs).pop().unwrap();
|
||||
a.perform(jobs);
|
||||
for _ in 0..500 {
|
||||
if std::path::Path::new(&path).exists() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
assert!(std::path::Path::new(&path).exists());
|
||||
assert_eq!(mode(&dir), expected, "{}", dir.display());
|
||||
a.shutdown();
|
||||
}
|
||||
assert_eq!(mode(&base.join("new")), 0o700);
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_failed_autosave_is_tried_again_without_another_change() {
|
||||
hooks();
|
||||
|
||||
Reference in New Issue
Block a user