]> git.lizzy.rs Git - rust.git/blob - src/librustc_data_structures/flock.rs
Merge remote-tracking branch 'upstream/master'
[rust.git] / src / librustc_data_structures / flock.rs
1 //! Simple file-locking apis for each OS.
2 //!
3 //! This is not meant to be in the standard library, it does nothing with
4 //! green/native threading. This is just a bare-bones enough solution for
5 //! librustdoc, it is not production quality at all.
6
7 #![allow(non_camel_case_types)]
8 #![allow(nonstandard_style)]
9
10 use std::io;
11 use std::path::Path;
12
13 cfg_if! {
14     if #[cfg(unix)] {
15         use std::ffi::{CString, OsStr};
16         use std::os::unix::prelude::*;
17
18         #[cfg(any(target_os = "linux", target_os = "android"))]
19         mod os {
20             #[repr(C)]
21             pub struct flock {
22                 pub l_type: libc::c_short,
23                 pub l_whence: libc::c_short,
24                 pub l_start: libc::off_t,
25                 pub l_len: libc::off_t,
26                 pub l_pid: libc::pid_t,
27
28                 // not actually here, but brings in line with freebsd
29                 pub l_sysid: libc::c_int,
30             }
31         }
32
33         #[cfg(target_os = "freebsd")]
34         mod os {
35             #[repr(C)]
36             pub struct flock {
37                 pub l_start: libc::off_t,
38                 pub l_len: libc::off_t,
39                 pub l_pid: libc::pid_t,
40                 pub l_type: libc::c_short,
41                 pub l_whence: libc::c_short,
42                 pub l_sysid: libc::c_int,
43             }
44         }
45
46         #[cfg(any(target_os = "dragonfly",
47                   target_os = "bitrig",
48                   target_os = "netbsd",
49                   target_os = "openbsd"))]
50         mod os {
51             #[repr(C)]
52             pub struct flock {
53                 pub l_start: libc::off_t,
54                 pub l_len: libc::off_t,
55                 pub l_pid: libc::pid_t,
56                 pub l_type: libc::c_short,
57                 pub l_whence: libc::c_short,
58
59                 // not actually here, but brings in line with freebsd
60                 pub l_sysid: libc::c_int,
61             }
62         }
63
64         #[cfg(target_os = "haiku")]
65         mod os {
66             #[repr(C)]
67             pub struct flock {
68                 pub l_type: libc::c_short,
69                 pub l_whence: libc::c_short,
70                 pub l_start: libc::off_t,
71                 pub l_len: libc::off_t,
72                 pub l_pid: libc::pid_t,
73
74                 // not actually here, but brings in line with freebsd
75                 pub l_sysid: libc::c_int,
76             }
77         }
78
79         #[cfg(any(target_os = "macos", target_os = "ios"))]
80         mod os {
81             #[repr(C)]
82             pub struct flock {
83                 pub l_start: libc::off_t,
84                 pub l_len: libc::off_t,
85                 pub l_pid: libc::pid_t,
86                 pub l_type: libc::c_short,
87                 pub l_whence: libc::c_short,
88
89                 // not actually here, but brings in line with freebsd
90                 pub l_sysid: libc::c_int,
91             }
92         }
93
94         #[cfg(target_os = "solaris")]
95         mod os {
96             #[repr(C)]
97             pub struct flock {
98                 pub l_type: libc::c_short,
99                 pub l_whence: libc::c_short,
100                 pub l_start: libc::off_t,
101                 pub l_len: libc::off_t,
102                 pub l_sysid: libc::c_int,
103                 pub l_pid: libc::pid_t,
104             }
105         }
106
107         #[derive(Debug)]
108         pub struct Lock {
109             fd: libc::c_int,
110         }
111
112         impl Lock {
113             pub fn new(p: &Path,
114                        wait: bool,
115                        create: bool,
116                        exclusive: bool)
117                        -> io::Result<Lock> {
118                 let os: &OsStr = p.as_ref();
119                 let buf = CString::new(os.as_bytes()).unwrap();
120                 let open_flags = if create {
121                     libc::O_RDWR | libc::O_CREAT
122                 } else {
123                     libc::O_RDWR
124                 };
125
126                 let fd = unsafe {
127                     libc::open(buf.as_ptr(), open_flags,
128                                libc::S_IRWXU as libc::c_int)
129                 };
130
131                 if fd < 0 {
132                     return Err(io::Error::last_os_error());
133                 }
134
135                 let lock_type = if exclusive {
136                     libc::F_WRLCK as libc::c_short
137                 } else {
138                     libc::F_RDLCK as libc::c_short
139                 };
140
141                 let flock = os::flock {
142                     l_start: 0,
143                     l_len: 0,
144                     l_pid: 0,
145                     l_whence: libc::SEEK_SET as libc::c_short,
146                     l_type: lock_type,
147                     l_sysid: 0,
148                 };
149                 let cmd = if wait { libc::F_SETLKW } else { libc::F_SETLK };
150                 let ret = unsafe {
151                     libc::fcntl(fd, cmd, &flock)
152                 };
153                 if ret == -1 {
154                     let err = io::Error::last_os_error();
155                     unsafe { libc::close(fd); }
156                     Err(err)
157                 } else {
158                     Ok(Lock { fd })
159                 }
160             }
161         }
162
163         impl Drop for Lock {
164             fn drop(&mut self) {
165                 let flock = os::flock {
166                     l_start: 0,
167                     l_len: 0,
168                     l_pid: 0,
169                     l_whence: libc::SEEK_SET as libc::c_short,
170                     l_type: libc::F_UNLCK as libc::c_short,
171                     l_sysid: 0,
172                 };
173                 unsafe {
174                     libc::fcntl(self.fd, libc::F_SETLK, &flock);
175                     libc::close(self.fd);
176                 }
177             }
178         }
179     } else if #[cfg(windows)] {
180         use std::mem;
181         use std::os::windows::prelude::*;
182         use std::os::windows::raw::HANDLE;
183         use std::fs::{File, OpenOptions};
184         use std::os::raw::{c_ulong, c_int};
185
186         type DWORD = c_ulong;
187         type BOOL = c_int;
188         type ULONG_PTR = usize;
189
190         type LPOVERLAPPED = *mut OVERLAPPED;
191         const LOCKFILE_EXCLUSIVE_LOCK: DWORD = 0x0000_0002;
192         const LOCKFILE_FAIL_IMMEDIATELY: DWORD = 0x0000_0001;
193
194         const FILE_SHARE_DELETE: DWORD = 0x4;
195         const FILE_SHARE_READ: DWORD = 0x1;
196         const FILE_SHARE_WRITE: DWORD = 0x2;
197
198         #[repr(C)]
199         struct OVERLAPPED {
200             Internal: ULONG_PTR,
201             InternalHigh: ULONG_PTR,
202             Offset: DWORD,
203             OffsetHigh: DWORD,
204             hEvent: HANDLE,
205         }
206
207         extern "system" {
208             fn LockFileEx(hFile: HANDLE,
209                           dwFlags: DWORD,
210                           dwReserved: DWORD,
211                           nNumberOfBytesToLockLow: DWORD,
212                           nNumberOfBytesToLockHigh: DWORD,
213                           lpOverlapped: LPOVERLAPPED) -> BOOL;
214         }
215
216         #[derive(Debug)]
217         pub struct Lock {
218             _file: File,
219         }
220
221         impl Lock {
222             pub fn new(p: &Path,
223                        wait: bool,
224                        create: bool,
225                        exclusive: bool)
226                        -> io::Result<Lock> {
227                 assert!(p.parent().unwrap().exists(),
228                     "Parent directory of lock-file must exist: {}",
229                     p.display());
230
231                 let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
232
233                 let mut open_options = OpenOptions::new();
234                 open_options.read(true)
235                             .share_mode(share_mode);
236
237                 if create {
238                     open_options.create(true)
239                                 .write(true);
240                 }
241
242                 debug!("Attempting to open lock file `{}`", p.display());
243                 let file = match open_options.open(p) {
244                     Ok(file) => {
245                         debug!("Lock file opened successfully");
246                         file
247                     }
248                     Err(err) => {
249                         debug!("Error opening lock file: {}", err);
250                         return Err(err)
251                     }
252                 };
253
254                 let ret = unsafe {
255                     let mut overlapped: OVERLAPPED = mem::zeroed();
256
257                     let mut dwFlags = 0;
258                     if !wait {
259                         dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
260                     }
261
262                     if exclusive {
263                         dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
264                     }
265
266                     debug!("Attempting to acquire lock on lock file `{}`",
267                            p.display());
268                     LockFileEx(file.as_raw_handle(),
269                                dwFlags,
270                                0,
271                                0xFFFF_FFFF,
272                                0xFFFF_FFFF,
273                                &mut overlapped)
274                 };
275                 if ret == 0 {
276                     let err = io::Error::last_os_error();
277                     debug!("Failed acquiring file lock: {}", err);
278                     Err(err)
279                 } else {
280                     debug!("Successfully acquired lock.");
281                     Ok(Lock { _file: file })
282                 }
283             }
284         }
285
286         // Note that we don't need a Drop impl on the Windows: The file is unlocked
287         // automatically when it's closed.
288     } else {
289         #[derive(Debug)]
290         pub struct Lock(());
291
292         impl Lock {
293             pub fn new(_p: &Path, _wait: bool, _create: bool, _exclusive: bool)
294                 -> io::Result<Lock>
295             {
296                 let msg = "file locks not supported on this platform";
297                 Err(io::Error::new(io::ErrorKind::Other, msg))
298             }
299         }
300     }
301 }
302
303 impl Lock {
304     pub fn panicking_new(p: &Path,
305                          wait: bool,
306                          create: bool,
307                          exclusive: bool)
308                          -> Lock {
309         Lock::new(p, wait, create, exclusive).unwrap_or_else(|err| {
310             panic!("could not lock `{}`: {}", p.display(), err);
311         })
312     }
313 }