Compare commits

...
2 changed files with 45 additions and 3 deletions
+28
View File
@@ -454,6 +454,21 @@ pub fn create_folder(dir: &Path) -> std::io::Result<()> {
builder.create(dir)
}
/// [`create_folder`] for a folder at a predictable place in a shared folder (the system's temporary
/// folder): refused when it already exists with any access for group or other, as another account
/// could have made it first and read, or replace, what is written into it.
pub fn create_private_folder(dir: &Path) -> std::io::Result<()> {
create_folder(dir)?;
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
if std::fs::metadata(dir)?.mode() & 0o077 != 0 {
return Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "the folder already exists and is open to other accounts"));
}
}
Ok(())
}
fn write_synced(path: &Path, bytes: &[u8]) -> std::io::Result<()> {
let mut f = std::fs::File::create(path)?;
f.write_all(bytes)?;
@@ -663,6 +678,19 @@ mod tests {
let _ = std::fs::remove_dir_all(&base);
}
#[cfg(unix)]
#[test]
fn a_private_folder_is_refused_when_it_already_exists_open() {
use std::os::unix::fs::PermissionsExt;
let base = folder("private-folder");
let dir = base.join("soundcraft-recordings-1");
create_private_folder(&dir).unwrap();
create_private_folder(&dir).unwrap();
std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o755)).unwrap();
assert!(create_private_folder(&dir).is_err());
let _ = std::fs::remove_dir_all(&base);
}
#[test]
fn a_take_still_recording_at_a_crash_is_placed_on_recovery() {
use crate::io::Take;
+17 -3
View File
@@ -155,7 +155,13 @@ impl SoundApp {
/// autosave folder. Returns the takes it records.
fn arm_recorder(&mut self) -> Result<Vec<soundcraft_engine::io::Take>, String> {
let name = soundcraft_engine::io::sanitize_name(&self.engine.session().name);
let unsaved_dir = self.autosave_dir.as_ref().map_or_else(std::env::temp_dir, |d| d.join(name));
// Without autosave storage the takes of a session never saved go to a folder of their own under
// the shared temporary folder, made private below, not into the shared folder itself.
let shared_temp = self.autosave_dir.is_none();
let unsaved_dir = self
.autosave_dir
.as_ref()
.map_or_else(|| std::env::temp_dir().join(format!("soundcraft-recordings-{}", std::process::id())), |d| d.join(name));
let Some(r) = &mut self.recorder else { return Err("no audio input device".into()) };
let plan = soundcraft_engine::io::plan_takes(&self.engine, &unsaved_dir, r.channels);
if plan.is_empty() {
@@ -163,9 +169,17 @@ impl SoundApp {
}
// The takes are unsaved work: the folder they go into under the autosave root is
// owner-only, like the autosaves, before the recorder makes anything in it.
if self.engine.path.is_none() && self.autosave_dir.is_some() {
if self.engine.path.is_none() {
if shared_temp {
soundcraft_engine::autosave::create_private_folder(&unsaved_dir).map_err(|e| format!("{}: {e}", unsaved_dir.display()))?;
}
for dir in plan.iter().filter_map(|(_, _, path)| path.parent()) {
soundcraft_engine::autosave::create_folder(dir).map_err(|e| format!("{}: {e}", dir.display()))?;
let made = if shared_temp {
soundcraft_engine::autosave::create_private_folder(dir)
} else {
soundcraft_engine::autosave::create_folder(dir)
};
made.map_err(|e| format!("{}: {e}", dir.display()))?;
}
}
let takes = plan