use std::convert::TryInto;
use std::fs::File;
use std::io;
use std::io::SeekFrom;
use std::io::SeekFrom::Start;
use super::*;
#[derive(Debug)]
pub enum FlockArg {
LockShared,
LockExclusive,
Unlock,
LockSharedNonblock,
LockExclusiveNonblock,
UnlockNonblock,
}
pub use FlockArg::*;
impl FileLocking for File {
fn trim_exclusive_lock_left(&self, old: u64, new: u64) -> io::Result<bool> {
if let Err(err) = self.lock_segment(UnlockNonblock, None, old) {
warn!(?err, offset = old, "error unlocking exclusive file lock");
}
self.lock_segment(LockExclusiveNonblock, None, new)
}
fn lock_segment(&self, arg: FlockArg, len: Option<u64>, offset: u64) -> io::Result<bool> {
lock_file_segment(
self,
arg,
len.map(|len| len.try_into().unwrap()),
Start(offset),
)
}
}
pub fn lock_file_segment(
file: &File,
arg: FlockArg,
len: Option<i64>,
whence: SeekFrom,
) -> io::Result<bool> {
let handle = HANDLE(file.as_raw_handle() as isize);
let Start(offset) = whence else {
unimplemented!("{:?}", whence)
};
let mut overlapped = OVERLAPPED {
..Default::default()
};
let offset_parts = HighAndLow::from(offset);
overlapped.Anonymous.Anonymous.Offset = offset_parts.low;
overlapped.Anonymous.Anonymous.OffsetHigh = offset_parts.high;
let lpoverlapped = &mut overlapped as *mut _;
let convert = |res: ::windows::core::Result<()>| match res {
Ok(()) => Ok(true),
Err(err)
if err.code().is_ok()
|| [
ERROR_LOCK_VIOLATION.into(),
ERROR_IO_PENDING.into(),
]
.contains(&err.code()) =>
{
Ok(false)
}
Err(err) => Err(err.into()),
};
let len = match len {
Some(len) => len.try_into().unwrap(),
None => MAX_LOCKFILE_OFFSET - offset,
};
let num_bytes_to_lock = HighAndLow::from(len);
if matches!(arg, Unlock | UnlockNonblock) {
let result = unsafe {
UnlockFileEx(
handle,
0,
num_bytes_to_lock.low,
num_bytes_to_lock.high,
lpoverlapped,
)
};
debug!(handle=?file.as_raw_handle(), arg=?&arg, offset, len, ?result, "UnlockFileEx");
return convert(result);
}
let mut dwflags = LOCK_FILE_FLAGS(0);
if matches!(arg, LockExclusive | LockExclusiveNonblock) {
dwflags |= LOCKFILE_EXCLUSIVE_LOCK;
}
if matches!(arg, LockSharedNonblock | LockExclusiveNonblock) {
dwflags |= LOCKFILE_FAIL_IMMEDIATELY;
}
let result = unsafe {
LockFileEx(
handle,
dwflags,
0,
num_bytes_to_lock.low,
num_bytes_to_lock.high,
lpoverlapped,
)
};
debug!(handle=?file.as_raw_handle(), arg=?&arg, offset, len, ?result, "UnlockFileEx");
#[allow(clippy::let_and_return)]
convert(result)
}
const MAX_LOCKFILE_OFFSET: u64 = u64::MAX;
#[derive(Debug)]
struct HighAndLow {
high: u32,
low: u32,
}
impl From<u64> for HighAndLow {
fn from(both: u64) -> Self {
Self {
high: (both >> 32) as u32,
low: both as u32,
}
}
}