Skip to main content

rustc_data_structures/
flock.rs

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.
6
7use std::fs::{File, OpenOptions};
8use std::io;
9use std::ops::Deref;
10use std::path::{Path, PathBuf};
11
12#[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)]
16    FdLocked { _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)]
21    Fallback(fallback::Lock),
22}
23
24impl Lock {
25    pub fn try_lock(p: &Path, create: bool, exclusive: bool) -> io::Result<Lock> {
26        let mut open_options = OpenOptions::new();
27        open_options.read(true).write(true).create(create);
28        #[cfg(unix)]
29        {
30            use std::os::unix::fs::OpenOptionsExt;
31            open_options.mode(0o600);
32        }
33
34        let file = open_options.open(p)?;
35
36        let res = if exclusive {
37            file.try_lock().map_err(io::Error::from)
38        } else {
39            file.try_lock_shared().map_err(io::Error::from)
40        };
41
42        match res {
43            Ok(()) => Ok(Lock::FdLocked { _file: file }),
44            Err(err) if #[allow(non_exhaustive_omitted_patterns)] match err.kind() {
    io::ErrorKind::Unsupported => true,
    _ => false,
}matches!(err.kind(), io::ErrorKind::Unsupported) => {
45                Ok(Lock::Fallback(fallback::Lock::try_lock(p, file, exclusive)?))
46            }
47            Err(err) => Err(err),
48        }
49    }
50
51    pub fn error_unsupported(err: &io::Error) -> bool {
52        #[cfg(windows)]
53        if err.raw_os_error() == Some(windows::Win32::Foundation::ERROR_INVALID_FUNCTION.0 as i32) {
54            // Not mapped to ErrorKind::Unsupported by libstd
55            return true;
56        }
57
58        #[allow(non_exhaustive_omitted_patterns)] match err.kind() {
    io::ErrorKind::Unsupported => true,
    _ => false,
}matches!(err.kind(), io::ErrorKind::Unsupported)
59    }
60}
61
62cfg_select! {
63    unix => {
64        mod unix;
65        use unix as fallback;
66    }
67    _ => {
68        mod unsupported;
69        use unsupported as fallback;
70    }
71}
72
73/// 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}
81
82impl LockedDir {
83    pub fn try_lock(
84        dir: PathBuf,
85        lock_file: &Path,
86        create: bool,
87        exclusive: bool,
88    ) -> io::Result<Self> {
89        Ok(LockedDir { dir, _lock_file: Lock::try_lock(lock_file, create, exclusive)? })
90    }
91}
92
93impl Deref for LockedDir {
94    type Target = Path;
95
96    fn deref(&self) -> &Path {
97        &self.dir
98    }
99}