//! Android's pinned std lacks file locks; retain identical advisory-lock semantics. use std::{fs::File, io}; pub(crate) fn lock(file: &File) -> io::Result<()> { #[cfg(target_os = "android")] return rustix::fs::flock(file, rustix::fs::FlockOperation::LockExclusive).map_err(Into::into); #[cfg(not(target_os = "android"))] file.lock() } pub(crate) fn try_lock(file: &File) -> io::Result<()> { #[cfg(target_os = "android")] return rustix::fs::flock(file, rustix::fs::FlockOperation::NonBlockingLockExclusive) .map_err(Into::into); #[cfg(not(target_os = "android"))] file.try_lock().map_err(|error| match error { std::fs::TryLockError::WouldBlock => io::ErrorKind::WouldBlock.into(), std::fs::TryLockError::Error(error) => error, }) } #[cfg(unix)] pub(crate) fn directory(directory: &cap_std::fs::Dir) -> io::Result { let file = directory.open(".")?.into_std(); lock(&file)?; Ok(file) } #[cfg(test)] mod tests { use super::*; #[test] fn independent_owners_contend_until_the_lock_is_dropped() { let temp = tempfile::tempdir().unwrap(); let path = temp.path().join("owner.lock"); let first = File::create(&path).unwrap(); let second = File::open(&path).unwrap(); lock(&first).unwrap(); assert_eq!( try_lock(&second).unwrap_err().kind(), io::ErrorKind::WouldBlock ); drop(first); try_lock(&second).unwrap(); } #[cfg(unix)] #[test] fn directory_gate_covers_file_creation_before_its_owner_lock() { let root = tempfile::tempdir().unwrap(); let dir = cap_std::fs::Dir::open_ambient_dir(root.path(), cap_std::ambient_authority()).unwrap(); let first = directory(&dir).unwrap(); let second = dir.open(".").unwrap().into_std(); assert_eq!( try_lock(&second).unwrap_err().kind(), io::ErrorKind::WouldBlock ); drop(first); try_lock(&second).unwrap(); } }