use std::io;
use std::os::unix::fs::DirBuilderExt;
use std::path::{Path, PathBuf};
pub fn create_in(parent: &Path, prefix: &str) -> io::Result<PathBuf> {
let seed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
static CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let call = CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let mut state = seed ^ (u128::from(std::process::id()) << 64) ^ (u128::from(call) << 32) | 1;
create_with(parent, prefix, || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state as u64
})
}
fn create_with(parent: &Path, prefix: &str, mut next: impl FnMut() -> u64) -> io::Result<PathBuf> {
for _ in 0..64 {
let dir = parent.join(format!("{prefix}-{}-{:016x}", std::process::id(), next()));
match std::fs::DirBuilder::new().mode(0o700).create(&dir) {
Ok(()) => return Ok(dir),
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
Err(e) => return Err(e),
}
}
Err(io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"no fresh `{prefix}-*` directory name under {}",
parent.display()
),
))
}
pub fn create(prefix: &str) -> io::Result<PathBuf> {
create_in(&std::env::temp_dir(), prefix)
}
#[cfg(test)]
mod tests {
use super::*;
use std::os::unix::fs::PermissionsExt;
#[test]
fn a_private_dir_is_owner_only_and_fresh_each_time() {
let a = create("vj-private-dir-test").unwrap();
let b = create("vj-private-dir-test").unwrap();
assert_ne!(a, b);
let mode = std::fs::metadata(&a).unwrap().permissions().mode() & 0o777;
assert_eq!(mode, 0o700, "{}", a.display());
let _ = std::fs::remove_dir(&a);
let _ = std::fs::remove_dir(&b);
}
#[test]
fn an_existing_path_is_skipped_never_reused() {
let parent = create("vj-private-dir-parent").unwrap();
let name = |n: u64| parent.join(format!("p-{}-{n:016x}", std::process::id()));
std::fs::create_dir(name(1)).unwrap();
std::fs::write(name(1).join("marker"), b"x").unwrap();
std::fs::write(name(2), b"x").unwrap();
let mut seq = [1u64, 1, 2, 3].into_iter();
let got = create_with(&parent, "p", || seq.next().unwrap()).unwrap();
assert_eq!(got, name(3));
assert_eq!(seq.next(), None, "all four candidate names consumed");
let mode = std::fs::metadata(&got).unwrap().permissions().mode() & 0o777;
assert_eq!(mode, 0o700);
assert!(
name(1).join("marker").exists(),
"pre-existing dir untouched"
);
assert_eq!(std::fs::read_dir(&got).unwrap().count(), 0);
let err = create_with(&parent, "p", || 1).unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::AlreadyExists);
let _ = std::fs::remove_dir_all(&parent);
}
}