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