| //! Simple file-locking apis for each OS. |
| //! |
| //! This is not meant to be in the standard library, it does nothing with |
| //! green/native threading. This is just a bare-bones enough solution for |
| //! librustdoc, it is not production quality at all. |
| |
| use std::fs::{File, OpenOptions}; |
| use std::io; |
| use std::ops::Deref; |
| use std::path::{Path, PathBuf}; |
| |
| #[derive(Debug)] |
| pub enum Lock { |
| /// A well behaved lock scoped to a single fd/handle and unlocked when closing it. |
| #[doc(hidden)] |
| FdLocked { _file: File }, |
| /// A fallback implementation which may for example be scoped to an entire process, |
| /// like legacy `fcntl(F_SETLK)` on Unix. This should only be used when `flock()` |
| /// or equivalent sane locking mechanism is unsupported by the OS. |
| #[doc(hidden)] |
| Fallback(fallback::Lock), |
| } |
| |
| impl Lock { |
| pub fn try_lock(p: &Path, create: bool, exclusive: bool) -> io::Result<Lock> { |
| let mut open_options = OpenOptions::new(); |
| open_options.read(true).write(true).create(create); |
| #[cfg(unix)] |
| { |
| use std::os::unix::fs::OpenOptionsExt; |
| open_options.mode(0o600); |
| } |
| |
| let file = open_options.open(p)?; |
| |
| let res = if exclusive { |
| file.try_lock().map_err(io::Error::from) |
| } else { |
| file.try_lock_shared().map_err(io::Error::from) |
| }; |
| |
| match res { |
| Ok(()) => Ok(Lock::FdLocked { _file: file }), |
| Err(err) if matches!(err.kind(), io::ErrorKind::Unsupported) => { |
| Ok(Lock::Fallback(fallback::Lock::try_lock(p, file, exclusive)?)) |
| } |
| Err(err) => Err(err), |
| } |
| } |
| |
| pub fn error_unsupported(err: &io::Error) -> bool { |
| #[cfg(windows)] |
| if err.raw_os_error() == Some(windows::Win32::Foundation::ERROR_INVALID_FUNCTION.0 as i32) { |
| // Not mapped to ErrorKind::Unsupported by libstd |
| return true; |
| } |
| |
| matches!(err.kind(), io::ErrorKind::Unsupported) |
| } |
| } |
| |
| cfg_select! { |
| unix => { |
| mod unix; |
| use unix as fallback; |
| } |
| _ => { |
| mod unsupported; |
| use unsupported as fallback; |
| } |
| } |
| |
| /// A directory together with a locked lockfile. |
| pub struct LockedDir { |
| dir: PathBuf, |
| /// `_lock_file` is never directly used, but its presence |
| /// alone has an effect, because the file will unlock when the session is |
| /// dropped. |
| _lock_file: Lock, |
| } |
| |
| impl LockedDir { |
| pub fn try_lock( |
| dir: PathBuf, |
| lock_file: &Path, |
| create: bool, |
| exclusive: bool, |
| ) -> io::Result<Self> { |
| Ok(LockedDir { dir, _lock_file: Lock::try_lock(lock_file, create, exclusive)? }) |
| } |
| } |
| |
| impl Deref for LockedDir { |
| type Target = Path; |
| |
| fn deref(&self) -> &Path { |
| &self.dir |
| } |
| } |