| use std::fs::{File, OpenOptions}; |
| use std::io; |
| #[cfg(unix)] |
| use std::os::unix::prelude::*; |
| use std::path::Path; |
| |
| #[derive(Debug)] |
| pub(super) 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 using the legacy `fcntl(F_SETLK)` which is scoped to |
| /// an entire process. This should only be used when `flock()` or equivalent sane |
| /// locking mechanism is unsupported by the OS. |
| #[doc(hidden)] |
| #[cfg(unix)] |
| FcntlFallback { _lock: FcntlLock }, |
| } |
| |
| impl Lock { |
| pub(super) fn new(p: &Path) -> io::Result<Lock> { |
| let mut open_options = OpenOptions::new(); |
| open_options.read(true).write(true).create(true); |
| #[cfg(unix)] |
| { |
| use std::os::unix::fs::OpenOptionsExt; |
| open_options.mode(0o600); |
| } |
| |
| let file = open_options.open(p)?; |
| |
| match file.lock() { |
| Ok(()) => Ok(Lock::FdLocked { _file: file }), |
| #[cfg(unix)] |
| Err(err) if matches!(err.kind(), io::ErrorKind::Unsupported) => { |
| Ok(Lock::FcntlFallback { _lock: FcntlLock::new(file)? }) |
| } |
| Err(err) => Err(err), |
| } |
| } |
| } |
| |
| #[derive(Debug)] |
| #[cfg(unix)] |
| pub(super) struct FcntlLock { |
| file: File, |
| } |
| |
| #[cfg(unix)] |
| impl FcntlLock { |
| fn new(file: File) -> io::Result<FcntlLock> { |
| let mut flock: libc::flock = unsafe { std::mem::zeroed() }; |
| #[cfg(not(all(target_os = "hurd", target_arch = "x86")))] |
| { |
| flock.l_type = libc::F_WRLCK as libc::c_short; |
| flock.l_whence = libc::SEEK_SET as libc::c_short; |
| } |
| #[cfg(all(target_os = "hurd", target_arch = "x86"))] |
| { |
| flock.l_type = libc::F_WRLCK as libc::c_int; |
| flock.l_whence = libc::SEEK_SET as libc::c_int; |
| } |
| flock.l_start = 0; |
| flock.l_len = 0; |
| |
| let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETLKW, &flock) }; |
| if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(FcntlLock { file }) } |
| } |
| } |
| |
| #[cfg(unix)] |
| impl Drop for FcntlLock { |
| fn drop(&mut self) { |
| let mut flock: libc::flock = unsafe { std::mem::zeroed() }; |
| #[cfg(not(all(target_os = "hurd", target_arch = "x86")))] |
| { |
| flock.l_type = libc::F_UNLCK as libc::c_short; |
| flock.l_whence = libc::SEEK_SET as libc::c_short; |
| } |
| #[cfg(all(target_os = "hurd", target_arch = "x86"))] |
| { |
| flock.l_type = libc::F_UNLCK as libc::c_int; |
| flock.l_whence = libc::SEEK_SET as libc::c_int; |
| } |
| flock.l_start = 0; |
| flock.l_len = 0; |
| |
| unsafe { |
| libc::fcntl(self.file.as_raw_fd(), libc::F_SETLK, &flock); |
| } |
| } |
| } |