]> git.lizzy.rs Git - rust.git/blob - src/librustc_data_structures/flock.rs
Auto merge of #53830 - davidtwco:issue-53228, r=nikomatsakis
[rust.git] / src / librustc_data_structures / flock.rs
1 // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Simple file-locking apis for each OS.
12 //!
13 //! This is not meant to be in the standard library, it does nothing with
14 //! green/native threading. This is just a bare-bones enough solution for
15 //! librustdoc, it is not production quality at all.
16
17 #![allow(non_camel_case_types)]
18 use std::path::Path;
19
20 pub use self::imp::Lock;
21
22 #[cfg(unix)]
23 mod imp {
24     use std::ffi::{CString, OsStr};
25     use std::os::unix::prelude::*;
26     use std::path::Path;
27     use std::io;
28     use libc;
29
30     #[cfg(any(target_os = "linux", target_os = "android"))]
31     mod os {
32         use libc;
33
34         #[repr(C)]
35         pub struct flock {
36             pub l_type: libc::c_short,
37             pub l_whence: libc::c_short,
38             pub l_start: libc::off_t,
39             pub l_len: libc::off_t,
40             pub l_pid: libc::pid_t,
41
42             // not actually here, but brings in line with freebsd
43             pub l_sysid: libc::c_int,
44         }
45
46         pub const F_RDLCK: libc::c_short = 0;
47         pub const F_WRLCK: libc::c_short = 1;
48         pub const F_UNLCK: libc::c_short = 2;
49         pub const F_SETLK: libc::c_int = 6;
50         pub const F_SETLKW: libc::c_int = 7;
51     }
52
53     #[cfg(target_os = "freebsd")]
54     mod os {
55         use libc;
56
57         #[repr(C)]
58         pub struct flock {
59             pub l_start: libc::off_t,
60             pub l_len: libc::off_t,
61             pub l_pid: libc::pid_t,
62             pub l_type: libc::c_short,
63             pub l_whence: libc::c_short,
64             pub l_sysid: libc::c_int,
65         }
66
67         pub const F_RDLCK: libc::c_short = 1;
68         pub const F_UNLCK: libc::c_short = 2;
69         pub const F_WRLCK: libc::c_short = 3;
70         pub const F_SETLK: libc::c_int = 12;
71         pub const F_SETLKW: libc::c_int = 13;
72     }
73
74     #[cfg(any(target_os = "dragonfly",
75               target_os = "bitrig",
76               target_os = "netbsd",
77               target_os = "openbsd"))]
78     mod os {
79         use libc;
80
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         pub const F_RDLCK: libc::c_short = 1;
94         pub const F_UNLCK: libc::c_short = 2;
95         pub const F_WRLCK: libc::c_short = 3;
96         pub const F_SETLK: libc::c_int = 8;
97         pub const F_SETLKW: libc::c_int = 9;
98     }
99
100     #[cfg(target_os = "haiku")]
101     mod os {
102         use libc;
103
104         #[repr(C)]
105         pub struct flock {
106             pub l_type: libc::c_short,
107             pub l_whence: libc::c_short,
108             pub l_start: libc::off_t,
109             pub l_len: libc::off_t,
110             pub l_pid: libc::pid_t,
111
112             // not actually here, but brings in line with freebsd
113             pub l_sysid: libc::c_int,
114         }
115
116         pub const F_RDLCK: libc::c_short = 0x0040;
117         pub const F_UNLCK: libc::c_short = 0x0200;
118         pub const F_WRLCK: libc::c_short = 0x0400;
119         pub const F_SETLK: libc::c_int = 0x0080;
120         pub const F_SETLKW: libc::c_int = 0x0100;
121     }
122
123     #[cfg(any(target_os = "macos", target_os = "ios"))]
124     mod os {
125         use libc;
126
127         #[repr(C)]
128         pub struct flock {
129             pub l_start: libc::off_t,
130             pub l_len: libc::off_t,
131             pub l_pid: libc::pid_t,
132             pub l_type: libc::c_short,
133             pub l_whence: libc::c_short,
134
135             // not actually here, but brings in line with freebsd
136             pub l_sysid: libc::c_int,
137         }
138
139         pub const F_RDLCK: libc::c_short = 1;
140         pub const F_UNLCK: libc::c_short = 2;
141         pub const F_WRLCK: libc::c_short = 3;
142         pub const F_SETLK: libc::c_int = 8;
143         pub const F_SETLKW: libc::c_int = 9;
144     }
145
146     #[cfg(target_os = "solaris")]
147     mod os {
148         use libc;
149
150         #[repr(C)]
151         pub struct flock {
152             pub l_type: libc::c_short,
153             pub l_whence: libc::c_short,
154             pub l_start: libc::off_t,
155             pub l_len: libc::off_t,
156             pub l_sysid: libc::c_int,
157             pub l_pid: libc::pid_t,
158         }
159
160         pub const F_RDLCK: libc::c_short = 1;
161         pub const F_WRLCK: libc::c_short = 2;
162         pub const F_UNLCK: libc::c_short = 3;
163         pub const F_SETLK: libc::c_int = 6;
164         pub const F_SETLKW: libc::c_int = 7;
165     }
166
167     #[derive(Debug)]
168     pub struct Lock {
169         fd: libc::c_int,
170     }
171
172     impl Lock {
173         pub fn new(p: &Path,
174                    wait: bool,
175                    create: bool,
176                    exclusive: bool)
177                    -> io::Result<Lock> {
178             let os: &OsStr = p.as_ref();
179             let buf = CString::new(os.as_bytes()).unwrap();
180             let open_flags = if create {
181                 libc::O_RDWR | libc::O_CREAT
182             } else {
183                 libc::O_RDWR
184             };
185
186             let fd = unsafe {
187                 libc::open(buf.as_ptr(), open_flags,
188                            libc::S_IRWXU as libc::c_int)
189             };
190
191             if fd < 0 {
192                 return Err(io::Error::last_os_error());
193             }
194
195             let lock_type = if exclusive {
196                 os::F_WRLCK
197             } else {
198                 os::F_RDLCK
199             };
200
201             let flock = os::flock {
202                 l_start: 0,
203                 l_len: 0,
204                 l_pid: 0,
205                 l_whence: libc::SEEK_SET as libc::c_short,
206                 l_type: lock_type,
207                 l_sysid: 0,
208             };
209             let cmd = if wait { os::F_SETLKW } else { os::F_SETLK };
210             let ret = unsafe {
211                 libc::fcntl(fd, cmd, &flock)
212             };
213             if ret == -1 {
214                 let err = io::Error::last_os_error();
215                 unsafe { libc::close(fd); }
216                 Err(err)
217             } else {
218                 Ok(Lock { fd: fd })
219             }
220         }
221     }
222
223     impl Drop for Lock {
224         fn drop(&mut self) {
225             let flock = os::flock {
226                 l_start: 0,
227                 l_len: 0,
228                 l_pid: 0,
229                 l_whence: libc::SEEK_SET as libc::c_short,
230                 l_type: os::F_UNLCK,
231                 l_sysid: 0,
232             };
233             unsafe {
234                 libc::fcntl(self.fd, os::F_SETLK, &flock);
235                 libc::close(self.fd);
236             }
237         }
238     }
239 }
240
241 #[cfg(windows)]
242 #[allow(nonstandard_style)]
243 mod imp {
244     use std::io;
245     use std::mem;
246     use std::os::windows::prelude::*;
247     use std::os::windows::raw::HANDLE;
248     use std::path::Path;
249     use std::fs::{File, OpenOptions};
250     use std::os::raw::{c_ulong, c_int};
251
252     type DWORD = c_ulong;
253     type BOOL = c_int;
254     type ULONG_PTR = usize;
255
256     type LPOVERLAPPED = *mut OVERLAPPED;
257     const LOCKFILE_EXCLUSIVE_LOCK: DWORD = 0x0000_0002;
258     const LOCKFILE_FAIL_IMMEDIATELY: DWORD = 0x0000_0001;
259
260     const FILE_SHARE_DELETE: DWORD = 0x4;
261     const FILE_SHARE_READ: DWORD = 0x1;
262     const FILE_SHARE_WRITE: DWORD = 0x2;
263
264     #[repr(C)]
265     struct OVERLAPPED {
266         Internal: ULONG_PTR,
267         InternalHigh: ULONG_PTR,
268         Offset: DWORD,
269         OffsetHigh: DWORD,
270         hEvent: HANDLE,
271     }
272
273     extern "system" {
274         fn LockFileEx(hFile: HANDLE,
275                       dwFlags: DWORD,
276                       dwReserved: DWORD,
277                       nNumberOfBytesToLockLow: DWORD,
278                       nNumberOfBytesToLockHigh: DWORD,
279                       lpOverlapped: LPOVERLAPPED) -> BOOL;
280     }
281
282     #[derive(Debug)]
283     pub struct Lock {
284         _file: File,
285     }
286
287     impl Lock {
288         pub fn new(p: &Path,
289                    wait: bool,
290                    create: bool,
291                    exclusive: bool)
292                    -> io::Result<Lock> {
293             assert!(p.parent().unwrap().exists(),
294                 "Parent directory of lock-file must exist: {}",
295                 p.display());
296
297             let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
298
299             let mut open_options = OpenOptions::new();
300             open_options.read(true)
301                         .share_mode(share_mode);
302
303             if create {
304                 open_options.create(true)
305                             .write(true);
306             }
307
308             debug!("Attempting to open lock file `{}`", p.display());
309             let file = match open_options.open(p) {
310                 Ok(file) => {
311                     debug!("Lock file opened successfully");
312                     file
313                 }
314                 Err(err) => {
315                     debug!("Error opening lock file: {}", err);
316                     return Err(err)
317                 }
318             };
319
320             let ret = unsafe {
321                 let mut overlapped: OVERLAPPED = mem::zeroed();
322
323                 let mut dwFlags = 0;
324                 if !wait {
325                     dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
326                 }
327
328                 if exclusive {
329                     dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
330                 }
331
332                 debug!("Attempting to acquire lock on lock file `{}`",
333                        p.display());
334                 LockFileEx(file.as_raw_handle(),
335                            dwFlags,
336                            0,
337                            0xFFFF_FFFF,
338                            0xFFFF_FFFF,
339                            &mut overlapped)
340             };
341             if ret == 0 {
342                 let err = io::Error::last_os_error();
343                 debug!("Failed acquiring file lock: {}", err);
344                 Err(err)
345             } else {
346                 debug!("Successfully acquired lock.");
347                 Ok(Lock { _file: file })
348             }
349         }
350     }
351
352     // Note that we don't need a Drop impl on the Windows: The file is unlocked
353     // automatically when it's closed.
354 }
355
356 impl imp::Lock {
357     pub fn panicking_new(p: &Path,
358                          wait: bool,
359                          create: bool,
360                          exclusive: bool)
361                          -> Lock {
362         Lock::new(p, wait, create, exclusive).unwrap_or_else(|err| {
363             panic!("could not lock `{}`: {}", p.display(), err);
364         })
365     }
366 }