#![cfg(unix)]
use std::io;
use std::path::Path;
pub(crate) const MAX_TMP_NAME_ATTEMPTS: usize = 8;
pub(crate) fn create_new_with_retry(
dir: &Path,
mut next_name: impl FnMut() -> String,
max_attempts: usize,
) -> io::Result<(std::fs::File, String)> {
for _ in 0..max_attempts {
let name = next_name();
match std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(dir.join(&name))
{
Ok(file) => return Ok((file, name)),
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
Err(e) => return Err(e),
}
}
Err(io::Error::new(
io::ErrorKind::AlreadyExists,
format!("{max_attempts} 回試行しても一意なファイル名を確保できませんでした"),
))
}
pub(crate) fn remove_own_tmp(file: &std::fs::File, path: &Path) {
use std::os::unix::fs::MetadataExt;
let Ok(held) = file.metadata() else {
return;
};
let Ok(on_disk) = std::fs::symlink_metadata(path) else {
return;
};
if on_disk.file_type().is_file() && on_disk.dev() == held.dev() && on_disk.ino() == held.ino() {
let _ = std::fs::remove_file(path);
}
}
static NAME_COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
pub(crate) fn name_entropy() -> (u32, u128, u64) {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let counter = NAME_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
(std::process::id(), nanos, counter)
}