1use std::io::SeekFrom;
2
3use super::*;
4
5fn seek_from_offset(seek_from: SeekFrom) -> i64 {
6 use SeekFrom::*;
7 match seek_from {
8 Start(offset) => offset as i64,
9 End(offset) | Current(offset) => offset,
10 }
11}
12
13fn seek_from_whence(seek_from: SeekFrom) -> libc::c_short {
14 use libc::*;
15 use SeekFrom::*;
16 (match seek_from {
17 Start(_) => SEEK_SET,
18 Current(_) => SEEK_CUR,
19 End(_) => SEEK_END,
20 }) as c_short
21}
22
23cfg_if! {
24 if #[cfg(any(target_os = "macos", target_os = "freebsd"))] {
25 use libc::flock as flock_struct;
26 } else {
27 use libc::flock64 as flock_struct;
28 }
29}
30
31pub(super) fn lock_file_segment(
33 file: &File,
34 arg: FlockArg,
35 len: Option<i64>,
36 whence: SeekFrom,
37) -> nix::Result<bool> {
38 debug!(?file, ?arg, ?len, ?whence, "locking file segment");
39 if let Some(len) = len {
40 if len == 0 {
42 return Ok(true);
43 }
44 }
45 #[allow(deprecated)]
46 let l_type = match arg {
47 LockShared | LockSharedNonblock => libc::F_RDLCK,
48 LockExclusive | LockExclusiveNonblock => libc::F_WRLCK,
49 Unlock | UnlockNonblock => libc::F_UNLCK,
50 _ => unimplemented!(),
52 };
53 let mut flock_arg: flock_struct = unsafe { std::mem::zeroed() };
54 flock_arg.l_start = seek_from_offset(whence);
55 flock_arg.l_len = len.unwrap_or(0);
56 #[allow(clippy::useless_conversion)]
57 let l_type = l_type.try_into().unwrap();
58 flock_arg.l_type = l_type;
59 flock_arg.l_whence = seek_from_whence(whence);
60 use libc::{F_OFD_SETLK as SetLock, F_OFD_SETLKW as SetLockWait};
61 #[allow(deprecated)]
62 let arg = match arg {
63 LockShared | LockExclusive => SetLockWait,
64 LockSharedNonblock | LockExclusiveNonblock | Unlock | UnlockNonblock => SetLock,
65 _ => unimplemented!(),
66 };
67 use nix::errno::Errno;
69 match Errno::result(unsafe { libc::fcntl(file.as_raw_fd(), arg, &flock_arg) }) {
74 Ok(_) => Ok(true),
75 Err(errno) if errno == Errno::EWOULDBLOCK || errno == Errno::EAGAIN => Ok(false),
76 Err(err) => {
77 error!(?err, "fcntl");
78 Err(err)
79 }
80 }
81}
82
83impl FileLocking for File {
84 fn trim_exclusive_lock_left(&self, old_left: u64, new_left: u64) -> io::Result<bool> {
85 if flocking() {
86 return Ok(true);
87 }
88 self.lock_segment(UnlockNonblock, Some(new_left - old_left), old_left)
90 }
91
92 fn lock_segment(&self, arg: FlockArg, len: Option<u64>, offset: u64) -> io::Result<bool> {
93 if flocking() {
94 return self.flock(arg);
95 }
96 Ok(lock_file_segment(
97 self,
98 arg,
99 len.map(|some| some.try_into().unwrap()),
100 Start(offset),
101 )?)
102 }
103}