extern crate kernel32;
extern crate winapi;
use std::fs::File;
use std::io::{Error, Result};
use std::os::windows::io::{AsRawHandle, FromRawHandle};
use std::ptr;
use std::mem;
pub fn duplicate(file: &File) -> Result<File> {
unsafe {
let mut handle = ptr::null_mut();
let current_process = kernel32::GetCurrentProcess();
let ret = kernel32::DuplicateHandle(current_process,
file.as_raw_handle(),
current_process,
&mut handle,
0,
true as winapi::BOOL,
winapi::DUPLICATE_SAME_ACCESS);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(File::from_raw_handle(handle))
}
}
}
pub fn lock_shared(file: &File) -> Result<()> {
lock_file(file, 0)
}
pub fn lock_exclusive(file: &File) -> Result<()> {
lock_file(file, winapi::LOCKFILE_EXCLUSIVE_LOCK)
}
pub fn lock_shared_nonblock(file: &File) -> Result<()> {
lock_file(file, winapi::LOCKFILE_FAIL_IMMEDIATELY)
}
pub fn lock_exclusive_nonblock(file: &File) -> Result<()> {
lock_file(file, winapi::LOCKFILE_EXCLUSIVE_LOCK | winapi::LOCKFILE_FAIL_IMMEDIATELY)
}
pub fn unlock(file: &File) -> Result<()> {
unsafe {
let ret = kernel32::UnlockFile(file.as_raw_handle(), 0, 0, !0, !0);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn lock_error() -> Error {
Error::from_raw_os_error(winapi::ERROR_LOCK_VIOLATION as i32)
}
fn lock_file(file: &File, flags: winapi::DWORD) -> Result<()> {
unsafe {
let mut overlapped = mem::zeroed();
let ret = kernel32::LockFileEx(file.as_raw_handle(), flags, 0, !0, !0, &mut overlapped);
if ret == 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
#[cfg(test)]
mod test {
extern crate tempdir;
use std::fs;
use std::os::windows::io::AsRawHandle;
use {FileExt, lock_contended_error};
#[test]
fn duplicate_new_handle() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file1 = fs::OpenOptions::new().write(true).create(true).open(&path).unwrap();
let file2 = file1.duplicate().unwrap();
assert!(file1.as_raw_handle() != file2.as_raw_handle());
}
#[test]
fn lock_duplicate_handle_independence() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file1 = fs::OpenOptions::new().read(true).create(true).open(&path).unwrap();
let file2 = file1.duplicate().unwrap();
file1.lock_shared().unwrap();
assert_eq!(file2.lock_exclusive_nonblock().unwrap_err().raw_os_error(),
lock_contended_error().raw_os_error());
file1.unlock().unwrap();
file2.lock_exclusive().unwrap();
}
#[test]
fn lock_non_reentrant() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file = fs::OpenOptions::new().read(true).create(true).open(&path).unwrap();
file.lock_exclusive().unwrap();
assert_eq!(file.lock_exclusive_nonblock().unwrap_err().raw_os_error(),
lock_contended_error().raw_os_error());
file.unlock().unwrap();
file.lock_shared().unwrap();
assert_eq!(file.lock_exclusive_nonblock().unwrap_err().raw_os_error(),
lock_contended_error().raw_os_error());
}
#[test]
fn lock_layering() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file = fs::OpenOptions::new().read(true).create(true).open(&path).unwrap();
file.lock_exclusive().unwrap();
file.lock_shared().unwrap();
file.lock_shared().unwrap();
assert_eq!(file.lock_exclusive_nonblock().unwrap_err().raw_os_error(),
lock_contended_error().raw_os_error());
file.unlock().unwrap();
assert_eq!(file.lock_exclusive_nonblock().unwrap_err().raw_os_error(),
lock_contended_error().raw_os_error());
file.unlock().unwrap();
assert_eq!(file.lock_exclusive_nonblock().unwrap_err().raw_os_error(),
lock_contended_error().raw_os_error());
file.unlock().unwrap();
file.lock_exclusive().unwrap();
}
#[test]
fn lock_layering_cleanup() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file1 = fs::OpenOptions::new().read(true).create(true).open(&path).unwrap();
let file2 = fs::OpenOptions::new().read(true).create(true).open(&path).unwrap();
file1.lock_shared().unwrap();
assert_eq!(file2.lock_exclusive_nonblock().unwrap_err().raw_os_error(),
lock_contended_error().raw_os_error());
drop(file1);
file2.lock_exclusive().unwrap();
}
#[test]
fn lock_duplicate_cleanup() {
let tempdir = tempdir::TempDir::new("fs2").unwrap();
let path = tempdir.path().join("fs2");
let file1 = fs::OpenOptions::new().read(true).create(true).open(&path).unwrap();
let file2 = file1.duplicate().unwrap();
file1.lock_shared().unwrap();
drop(file1);
assert_eq!(file2.lock_exclusive_nonblock().unwrap_err().raw_os_error(),
lock_contended_error().raw_os_error());
}
}