mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 02:37:01 -06:00
Data Recovery: the recovery folders are owner-only on Unix
Recovery copies are unsaved documents. FolderStore made its folders with create_dir_all, so on Unix they got the default mode and other accounts on the machine could read the copies. Folders the store creates are now 0o700. A store folder that already exists (it can be one picked in Preferences) keeps its mode; the area made inside it is owner-only. The same change as storytold/pdfcraft#3320.
This commit is contained in:
@@ -252,6 +252,18 @@ impl FolderStore {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create area folder `dir`, and the store's folder when it is missing. On Unix every folder
|
||||
/// made here is owner-only (`0o700`): the copies are unsaved documents, and with the default
|
||||
/// mode other accounts on the machine could read them. A store folder that already exists
|
||||
/// keeps its mode, since it can be one the user picked in Preferences.
|
||||
fn create_area(dir: &std::path::Path) -> std::result::Result<(), String> {
|
||||
let mut builder = std::fs::DirBuilder::new();
|
||||
builder.recursive(true);
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::DirBuilderExt::mode(&mut builder, 0o700);
|
||||
builder.create(dir).map_err(|e| format!("{}: {e}", dir.display()))
|
||||
}
|
||||
|
||||
/// The plain names of the files (or, `dirs`, folders) in `dir`; none when it doesn't exist.
|
||||
fn names(dir: &std::path::Path, dirs: bool) -> std::result::Result<Vec<String>, String> {
|
||||
let entries = match std::fs::read_dir(dir) {
|
||||
@@ -285,7 +297,7 @@ impl RecoveryStore for FolderStore {
|
||||
fn write(&self, name: &str, bytes: &[u8]) -> std::result::Result<(), String> {
|
||||
let f = self.file(name)?;
|
||||
if let Some(dir) = f.parent() {
|
||||
std::fs::create_dir_all(dir).map_err(|e| format!("{}: {e}", dir.display()))?;
|
||||
Self::create_area(dir)?;
|
||||
}
|
||||
vectorcraft_format::write_atomic(&f, bytes).map_err(|e| format!("{}: {e}", f.display()))
|
||||
}
|
||||
@@ -304,7 +316,7 @@ impl RecoveryStore for FolderStore {
|
||||
}
|
||||
fn lock(&self, area: &str) -> std::result::Result<Lock, String> {
|
||||
let dir = self.area(area)?;
|
||||
std::fs::create_dir_all(&dir).map_err(|e| format!("{}: {e}", dir.display()))?;
|
||||
Self::create_area(&dir)?;
|
||||
let path = dir.join(LOCK_FILE);
|
||||
let file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
|
||||
@@ -363,6 +363,33 @@ fn folder_areas_are_locked_by_running_apps() {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// The copies are unsaved documents: on Unix the folders the store makes are owner-only, and a
|
||||
/// store folder that already exists (one picked in Preferences) keeps its mode.
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn the_folder_store_makes_owner_only_folders() {
|
||||
use std::os::unix::fs::PermissionsExt as _;
|
||||
let mode = |p: &std::path::Path| std::fs::metadata(p).unwrap().permissions().mode() & 0o777;
|
||||
let base = std::env::temp_dir().join(format!("vc-recovery-modes-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
// A store folder that doesn't exist yet is made with the first copy, owner-only like its area.
|
||||
let new = base.join("new");
|
||||
let store = FolderStore::new(&new);
|
||||
store.write("1-1/Untitled-1-1.vectorcraft", b"copy").unwrap();
|
||||
assert_eq!(mode(&new), 0o700);
|
||||
assert_eq!(mode(&new.join("1-1")), 0o700);
|
||||
assert!(matches!(store.lock("1-2"), Ok(cmd::recovery::Lock::Held(_))));
|
||||
assert_eq!(mode(&new.join("1-2")), 0o700);
|
||||
// A folder that already exists keeps its mode; the area made inside it is owner-only.
|
||||
let picked = base.join("picked");
|
||||
std::fs::create_dir_all(&picked).unwrap();
|
||||
std::fs::set_permissions(&picked, std::fs::Permissions::from_mode(0o755)).unwrap();
|
||||
FolderStore::new(&picked).write("2-1/Untitled-1-1.vectorcraft", b"copy").unwrap();
|
||||
assert_eq!(mode(&picked), 0o755);
|
||||
assert_eq!(mode(&picked.join("2-1")), 0o700);
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saving_reverting_or_closing_removes_the_copy() {
|
||||
let dir = std::env::temp_dir().join(format!("vc-recovery-save-{}", std::process::id()));
|
||||
|
||||
Reference in New Issue
Block a user