1 use libc::{LOCK_EX, LOCK_NB, LOCK_UN};
2 use std::fs::{File, OpenOptions};
3 use std::io;
4 use std::os::unix::io::{AsRawFd, RawFd};
5 use std::path::Path;
6 
7 use crate::error::*;
8 
9 #[derive(Debug)]
10 pub(crate) struct RawNamedLock {
11     lock_file: File,
12 }
13 
14 impl RawNamedLock {
create(lock_path: &Path) -> Result<RawNamedLock>15     pub(crate) fn create(lock_path: &Path) -> Result<RawNamedLock> {
16         let lock_file = OpenOptions::new()
17             .write(true)
18             .create_new(true)
19             .open(&lock_path)
20             .or_else(|_| OpenOptions::new().write(true).open(&lock_path))
21             .map_err(Error::CreateFailed)?;
22 
23         Ok(RawNamedLock {
24             lock_file,
25         })
26     }
27 
try_lock(&self) -> Result<()>28     pub(crate) fn try_lock(&self) -> Result<()> {
29         unsafe { flock(self.lock_file.as_raw_fd(), LOCK_EX | LOCK_NB) }
30     }
31 
lock(&self) -> Result<()>32     pub(crate) fn lock(&self) -> Result<()> {
33         unsafe { flock(self.lock_file.as_raw_fd(), LOCK_EX) }
34     }
35 
unlock(&self) -> Result<()>36     pub(crate) fn unlock(&self) -> Result<()> {
37         unsafe { flock(self.lock_file.as_raw_fd(), LOCK_UN) }
38     }
39 }
40 
flock(fd: RawFd, operation: i32) -> Result<()>41 unsafe fn flock(fd: RawFd, operation: i32) -> Result<()> {
42     loop {
43         let rc = libc::flock(fd, operation);
44 
45         if rc < 0 {
46             let err = io::Error::last_os_error();
47 
48             if err.kind() == io::ErrorKind::Interrupted {
49                 continue;
50             } else if err.kind() == io::ErrorKind::WouldBlock {
51                 return Err(Error::WouldBlock);
52             } else if (operation & LOCK_EX) == LOCK_EX {
53                 return Err(Error::LockFailed);
54             } else if (operation & LOCK_UN) == LOCK_UN {
55                 return Err(Error::UnlockFailed);
56             }
57         }
58 
59         break;
60     }
61 
62     Ok(())
63 }
64