1//! Simple file-locking apis for each OS.
2//!
3//! This is not meant to be in the standard library, it does nothing with
4//! green/native threading. This is just a bare-bones enough solution for
5//! librustdoc, it is not production quality at all.
67use std::fs::{File, OpenOptions};
8use std::io;
9use std::ops::Deref;
10use std::path::{Path, PathBuf};
1112#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Lock {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::FdLocked { _file: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"FdLocked", "_file", &__self_0),
Self::Fallback(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Fallback", &__self_0),
}
}
}Debug)]
13pub enum Lock {
14/// A well behaved lock scoped to a single fd/handle and unlocked when closing it.
15#[doc(hidden)]
16FdLocked { _file: File },
17/// A fallback implementation which may for example be scoped to an entire process,
18 /// like legacy `fcntl(F_SETLK)` on Unix. This should only be used when `flock()`
19 /// or equivalent sane locking mechanism is unsupported by the OS.
20#[doc(hidden)]
21Fallback(fallback::Lock),
22}
2324impl Lock {
25pub fn try_lock(p: &Path, create: bool, exclusive: bool) -> io::Result<Lock> {
26let mut open_options = OpenOptions::new();
27open_options.read(true).write(true).create(create);
28#[cfg(unix)]
29{
30use std::os::unix::fs::OpenOptionsExt;
31open_options.mode(0o600);
32 }
3334let file = open_options.open(p)?;
3536let res = if exclusive {
37file.try_lock().map_err(io::Error::from)
38 } else {
39file.try_lock_shared().map_err(io::Error::from)
40 };
4142match res {
43Ok(()) => Ok(Lock::FdLocked { _file: file }),
44Err(err) if #[allow(non_exhaustive_omitted_patterns)] match err.kind() {
io::ErrorKind::Unsupported => true,
_ => false,
}matches!(err.kind(), io::ErrorKind::Unsupported) => {
45Ok(Lock::Fallback(fallback::Lock::try_lock(p, file, exclusive)?))
46 }
47Err(err) => Err(err),
48 }
49 }
5051pub fn error_unsupported(err: &io::Error) -> bool {
52#[cfg(windows)]
53if err.raw_os_error() == Some(windows::Win32::Foundation::ERROR_INVALID_FUNCTION.0 as i32) {
54// Not mapped to ErrorKind::Unsupported by libstd
55return true;
56 }
5758#[allow(non_exhaustive_omitted_patterns)] match err.kind() {
io::ErrorKind::Unsupported => true,
_ => false,
}matches!(err.kind(), io::ErrorKind::Unsupported)59 }
60}
6162cfg_select! {
63 unix => {
64mod unix;
65use unixas fallback;
66 }
67_ => {
68mod unsupported;
69use unsupported as fallback;
70 }
71}
7273/// A directory together with a locked lockfile.
74pub struct LockedDir {
75 dir: PathBuf,
76/// `_lock_file` is never directly used, but its presence
77 /// alone has an effect, because the file will unlock when the session is
78 /// dropped.
79_lock_file: Lock,
80}
8182impl LockedDir {
83pub fn try_lock(
84 dir: PathBuf,
85 lock_file: &Path,
86 create: bool,
87 exclusive: bool,
88 ) -> io::Result<Self> {
89Ok(LockedDir { dir, _lock_file: Lock::try_lock(lock_file, create, exclusive)? })
90 }
91}
9293impl Dereffor LockedDir {
94type Target = Path;
9596fn deref(&self) -> &Path {
97&self.dir
98 }
99}