1use libc::{fcntl, off_t, F_RDLCK, F_SETLK, F_SETLKW, F_UNLCK, F_WRLCK, SEEK_SET};
3
4use std::fs::File;
5use std::io::{self, Error, ErrorKind};
6use std::ops::Deref;
7use std::os::raw::c_short;
8use std::os::unix::io::AsRawFd;
9
10use crate::{FileGuard, Lock};
11
12pub unsafe fn raw_file_lock(
19 f: &File,
20 lock: Option<Lock>,
21 off: usize,
22 len: usize,
23 wait: bool,
24) -> io::Result<()> {
25 if len == 0 {
26 return Err(ErrorKind::InvalidInput.into());
27 }
28
29 let op = match wait {
30 true => F_SETLKW,
31 false => F_SETLK,
32 };
33
34 let lock = libc::flock {
35 l_start: off as off_t,
36 l_len: len as off_t,
37 l_pid: 0,
38 l_type: match lock {
39 Some(Lock::Shared) => F_RDLCK as c_short,
40 Some(Lock::Exclusive) => F_WRLCK as c_short,
41 None => F_UNLCK as c_short,
42 },
43 l_whence: SEEK_SET as c_short,
44 #[cfg(any(target_os = "freebsd", target_os = "solaris", target_os = "illumos"))]
45 l_sysid: 0,
46 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
47 l_pad: [0; 4],
48 };
49
50 loop {
51 let rc = fcntl(f.as_raw_fd(), op, &lock);
52 if rc == -1 {
53 let err = Error::last_os_error();
54 if err.kind() != ErrorKind::Interrupted {
55 break Err(err);
56 }
57 } else {
58 break Ok(());
59 }
60 }
61}
62
63pub unsafe fn raw_file_downgrade(f: &File, off: usize, len: usize) -> io::Result<()> {
69 raw_file_lock(f, Some(Lock::Shared), off, len, false)
70}
71
72pub trait FileGuardExt {
76 fn upgrade(&mut self) -> io::Result<()>;
84
85 fn try_upgrade(&mut self) -> io::Result<()>;
94}
95
96impl<T> FileGuardExt for FileGuard<T>
97where
98 T: Deref<Target = File>,
99{
100 fn upgrade(&mut self) -> io::Result<()> {
101 if self.is_shared() {
102 unsafe {
103 raw_file_lock(
104 &self.file,
105 Some(Lock::Exclusive),
106 self.offset,
107 self.len,
108 true,
109 )?;
110 }
111 self.lock = Lock::Exclusive;
112 }
113 Ok(())
114 }
115
116 fn try_upgrade(&mut self) -> io::Result<()> {
117 if self.is_shared() {
118 unsafe {
119 raw_file_lock(
120 &self.file,
121 Some(Lock::Exclusive),
122 self.offset,
123 self.len,
124 false,
125 )?;
126 }
127 self.lock = Lock::Exclusive;
128 }
129 Ok(())
130 }
131}