Skip to main content

rustdoc/html/render/write_shared/
flock.rs

1use std::fs::{File, OpenOptions};
2use std::io;
3#[cfg(unix)]
4use std::os::unix::prelude::*;
5use std::path::Path;
6
7#[derive(Debug)]
8pub(super) enum Lock {
9    /// A well behaved lock scoped to a single fd/handle and unlocked when closing it.
10    #[doc(hidden)]
11    FdLocked { _file: File },
12    /// A fallback implementation using the legacy `fcntl(F_SETLK)` which is scoped to
13    /// an entire process. This should only be used when `flock()` or equivalent sane
14    /// locking mechanism is unsupported by the OS.
15    #[doc(hidden)]
16    #[cfg(unix)]
17    FcntlFallback { _lock: FcntlLock },
18}
19
20impl Lock {
21    pub(super) fn new(p: &Path) -> io::Result<Lock> {
22        let mut open_options = OpenOptions::new();
23        open_options.read(true).write(true).create(true);
24        #[cfg(unix)]
25        {
26            use std::os::unix::fs::OpenOptionsExt;
27            open_options.mode(0o600);
28        }
29
30        let file = open_options.open(p)?;
31
32        match file.lock() {
33            Ok(()) => Ok(Lock::FdLocked { _file: file }),
34            #[cfg(unix)]
35            Err(err) if matches!(err.kind(), io::ErrorKind::Unsupported) => {
36                Ok(Lock::FcntlFallback { _lock: FcntlLock::new(file)? })
37            }
38            Err(err) => Err(err),
39        }
40    }
41}
42
43#[derive(Debug)]
44#[cfg(unix)]
45pub(super) struct FcntlLock {
46    file: File,
47}
48
49#[cfg(unix)]
50impl FcntlLock {
51    fn new(file: File) -> io::Result<FcntlLock> {
52        let mut flock: libc::flock = unsafe { std::mem::zeroed() };
53        #[cfg(not(all(target_os = "hurd", target_arch = "x86")))]
54        {
55            flock.l_type = libc::F_WRLCK as libc::c_short;
56            flock.l_whence = libc::SEEK_SET as libc::c_short;
57        }
58        #[cfg(all(target_os = "hurd", target_arch = "x86"))]
59        {
60            flock.l_type = libc::F_WRLCK as libc::c_int;
61            flock.l_whence = libc::SEEK_SET as libc::c_int;
62        }
63        flock.l_start = 0;
64        flock.l_len = 0;
65
66        let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETLKW, &flock) };
67        if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(FcntlLock { file }) }
68    }
69}
70
71#[cfg(unix)]
72impl Drop for FcntlLock {
73    fn drop(&mut self) {
74        let mut flock: libc::flock = unsafe { std::mem::zeroed() };
75        #[cfg(not(all(target_os = "hurd", target_arch = "x86")))]
76        {
77            flock.l_type = libc::F_UNLCK as libc::c_short;
78            flock.l_whence = libc::SEEK_SET as libc::c_short;
79        }
80        #[cfg(all(target_os = "hurd", target_arch = "x86"))]
81        {
82            flock.l_type = libc::F_UNLCK as libc::c_int;
83            flock.l_whence = libc::SEEK_SET as libc::c_int;
84        }
85        flock.l_start = 0;
86        flock.l_len = 0;
87
88        unsafe {
89            libc::fcntl(self.file.as_raw_fd(), libc::F_SETLK, &flock);
90        }
91    }
92}