1use std::fs::File;
2use std::io::Error;
3use std::os::unix::io::{AsRawFd, RawFd};
4
5use crate::{AdvisoryFileLock, FileLockError, FileLockMode};
6
7impl AdvisoryFileLock for File {
8 fn lock(&self, file_lock_mode: FileLockMode) -> Result<(), FileLockError> {
9 self.as_raw_fd().lock(file_lock_mode)
10 }
11
12 fn try_lock(&self, file_lock_mode: FileLockMode) -> Result<(), FileLockError> {
13 self.as_raw_fd().try_lock(file_lock_mode)
14 }
15
16 fn unlock(&self) -> Result<(), FileLockError> {
17 self.as_raw_fd().unlock()
18 }
19}
20
21impl AdvisoryFileLock for RawFd {
22 fn lock(&self, file_lock_mode: FileLockMode) -> Result<(), FileLockError> {
23 lock_file(*self, file_lock_mode, false)
24 }
25
26 fn try_lock(&self, file_lock_mode: FileLockMode) -> Result<(), FileLockError> {
27 lock_file(*self, file_lock_mode, true)
28 }
29
30 fn unlock(&self) -> Result<(), FileLockError> {
31 unlock_file(*self)
32 }
33}
34
35fn lock_file(
36 raw_fd: RawFd,
37 file_lock_mode: FileLockMode,
38 immediate: bool,
39) -> Result<(), FileLockError> {
40 let mut flags = match file_lock_mode {
41 FileLockMode::Shared => libc::LOCK_SH,
42 FileLockMode::Exclusive => libc::LOCK_EX,
43 };
44 if immediate {
45 flags |= libc::LOCK_NB;
46 }
47
48 let result = unsafe { libc::flock(raw_fd, flags) };
49 if result != 0 {
50 let last_os_error = Error::last_os_error();
51 return Err(match last_os_error.raw_os_error() {
52 Some(code) if code == libc::EWOULDBLOCK => FileLockError::AlreadyLocked,
53 _ => FileLockError::Io(last_os_error),
54 });
55 }
56
57 Ok(())
58}
59
60fn unlock_file(raw_fd: RawFd) -> Result<(), FileLockError> {
61 let result = unsafe { libc::flock(raw_fd, libc::LOCK_UN) };
62 if result == 0 {
63 Ok(())
64 } else {
65 Err(FileLockError::Io(Error::last_os_error()))
66 }
67}