#![allow(non_snake_case, non_camel_case_types, clippy::upper_case_acronyms)]
use std::ffi::c_void;
use std::io;
use std::os::windows::ffi::OsStrExt as _;
use std::path::Path;
pub(crate) type BOOL = i32;
pub(crate) type DWORD = u32;
pub(crate) type HANDLE = *mut c_void;
pub(crate) const INVALID_HANDLE_VALUE: HANDLE = -1_isize as HANDLE;
pub(crate) const GENERIC_WRITE: DWORD = 0x4000_0000;
pub(crate) const FILE_SHARE_READ: DWORD = 0x0000_0001;
pub(crate) const FILE_SHARE_WRITE: DWORD = 0x0000_0002;
pub(crate) const FILE_SHARE_DELETE: DWORD = 0x0000_0004;
pub(crate) const FILE_ATTRIBUTE_NORMAL: DWORD = 0x0000_0080;
pub(crate) const FILE_FLAG_BACKUP_SEMANTICS: DWORD = 0x0200_0000;
#[cfg(any(test, feature = "test-seams"))]
pub(crate) const PAGE_READONLY: DWORD = 0x02;
pub(crate) const PAGE_WRITECOPY: DWORD = 0x08;
pub(crate) const SECTION_MAP_READ: DWORD = 0x0004;
pub(crate) const FILE_MAP_READ: DWORD = SECTION_MAP_READ;
pub(crate) const OPEN_EXISTING: DWORD = 3;
pub(crate) const LOCKFILE_FAIL_IMMEDIATELY: DWORD = 0x0000_0001;
pub(crate) const LOCKFILE_EXCLUSIVE_LOCK: DWORD = 0x0000_0002;
#[cfg(test)]
pub(crate) const ERROR_ACCESS_DENIED: DWORD = 5;
pub(crate) const ERROR_LOCK_VIOLATION: DWORD = 33;
pub(crate) const ERROR_IO_PENDING: DWORD = 997;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
struct OVERLAPPED {
Internal: usize,
InternalHigh: usize,
Offset: DWORD,
OffsetHigh: DWORD,
hEvent: HANDLE,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
struct BY_HANDLE_FILE_INFORMATION {
dwFileAttributes: DWORD,
ftCreationTime: [DWORD; 2],
ftLastAccessTime: [DWORD; 2],
ftLastWriteTime: [DWORD; 2],
dwVolumeSerialNumber: DWORD,
nFileSizeHigh: DWORD,
nFileSizeLow: DWORD,
nNumberOfLinks: DWORD,
nFileIndexHigh: DWORD,
nFileIndexLow: DWORD,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
struct PSAPI_WORKING_SET_EX_INFORMATION {
VirtualAddress: *mut c_void,
VirtualAttributes: usize,
}
const WORKING_SET_EX_VALID: usize = 1;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
struct PROCESS_MEMORY_COUNTERS_EX {
cb: DWORD,
PageFaultCount: DWORD,
PeakWorkingSetSize: usize,
WorkingSetSize: usize,
QuotaPeakPagedPoolUsage: usize,
QuotaPagedPoolUsage: usize,
QuotaPeakNonPagedPoolUsage: usize,
QuotaNonPagedPoolUsage: usize,
PagefileUsage: usize,
PeakPagefileUsage: usize,
PrivateUsage: usize,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
struct SYSTEM_INFO {
wProcessorArchitecture: u16,
wReserved: u16,
dwPageSize: DWORD,
lpMinimumApplicationAddress: *mut c_void,
lpMaximumApplicationAddress: *mut c_void,
dwActiveProcessorMask: usize,
dwNumberOfProcessors: DWORD,
dwProcessorType: DWORD,
dwAllocationGranularity: DWORD,
wProcessorLevel: u16,
wProcessorRevision: u16,
}
#[cfg(any(feature = "graph-cypher", test))]
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x0000_0002;
#[cfg(any(feature = "graph-cypher", test))]
#[link(name = "bcrypt")]
unsafe extern "system" {
fn BCryptGenRandom(
hAlgorithm: *mut c_void,
pbBuffer: *mut u8,
cbBuffer: DWORD,
dwFlags: DWORD,
) -> i32;
}
#[cfg(any(feature = "graph-cypher", test))]
pub(crate) fn fill_entropy(output: &mut [u8]) -> io::Result<()> {
let length = DWORD::try_from(output.len())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "entropy buffer exceeds DWORD"))?;
let status = unsafe {
BCryptGenRandom(
std::ptr::null_mut(),
output.as_mut_ptr(),
length,
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
)
};
if status != 0 {
return Err(io::Error::other(format!(
"BCryptGenRandom failed: NTSTATUS {status:#x}"
)));
}
Ok(())
}
unsafe extern "system" {
fn CreateFileW(
lpFileName: *const u16,
dwDesiredAccess: DWORD,
dwShareMode: DWORD,
lpSecurityAttributes: *mut c_void,
dwCreationDisposition: DWORD,
dwFlagsAndAttributes: DWORD,
hTemplateFile: HANDLE,
) -> HANDLE;
fn FlushFileBuffers(hFile: HANDLE) -> BOOL;
fn CloseHandle(hObject: HANDLE) -> BOOL;
fn CreateFileMappingW(
hFile: HANDLE,
lpFileMappingAttributes: *mut c_void,
flProtect: DWORD,
dwMaximumSizeHigh: DWORD,
dwMaximumSizeLow: DWORD,
lpName: *const u16,
) -> HANDLE;
fn MapViewOfFile(
hFileMappingObject: HANDLE,
dwDesiredAccess: DWORD,
dwFileOffsetHigh: DWORD,
dwFileOffsetLow: DWORD,
dwNumberOfBytesToMap: usize,
) -> *mut c_void;
fn UnmapViewOfFile(lpBaseAddress: *const c_void) -> BOOL;
fn GetDiskFreeSpaceExW(
lpDirectoryName: *const u16,
lpFreeBytesAvailableToCaller: *mut u64,
lpTotalNumberOfBytes: *mut u64,
lpTotalNumberOfFreeBytes: *mut u64,
) -> BOOL;
fn GetCurrentProcess() -> HANDLE;
fn GetFileInformationByHandle(
hFile: HANDLE,
lpFileInformation: *mut BY_HANDLE_FILE_INFORMATION,
) -> BOOL;
fn LockFileEx(
hFile: HANDLE,
dwFlags: DWORD,
dwReserved: DWORD,
nNumberOfBytesToLockLow: DWORD,
nNumberOfBytesToLockHigh: DWORD,
lpOverlapped: *mut OVERLAPPED,
) -> BOOL;
fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
fn K32QueryWorkingSetEx(hProcess: HANDLE, pv: *mut c_void, cb: DWORD) -> BOOL;
fn K32GetProcessMemoryInfo(
hProcess: HANDLE,
ppsmemCounters: *mut PROCESS_MEMORY_COUNTERS_EX,
cb: DWORD,
) -> BOOL;
#[cfg(any(test, feature = "test-seams"))]
fn VirtualProtect(
lpAddress: *mut c_void,
dwSize: usize,
flNewProtect: DWORD,
lpflOldProtect: *mut DWORD,
) -> BOOL;
}
pub(crate) fn wide(path: &Path) -> io::Result<Vec<u16>> {
let mut encoded: Vec<u16> = path.as_os_str().encode_wide().collect();
if encoded.contains(&0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"store path contains an interior NUL",
));
}
encoded.push(0);
Ok(encoded)
}
#[derive(Debug)]
pub(crate) struct OwnedHandle(HANDLE);
unsafe impl Send for OwnedHandle {}
unsafe impl Sync for OwnedHandle {}
impl OwnedHandle {
fn adopt(raw: HANDLE) -> io::Result<Self> {
if raw.is_null() || raw == INVALID_HANDLE_VALUE {
return Err(io::Error::last_os_error());
}
Ok(Self(raw))
}
const fn raw(&self) -> HANDLE {
self.0
}
}
impl Drop for OwnedHandle {
fn drop(&mut self) {
unsafe {
CloseHandle(self.0);
}
}
}
fn open(
path: &Path,
access: DWORD,
share: DWORD,
disposition: DWORD,
flags: DWORD,
) -> io::Result<OwnedHandle> {
let encoded = wide(path)?;
let raw = unsafe {
CreateFileW(
encoded.as_ptr(),
access,
share,
std::ptr::null_mut(),
disposition,
flags,
std::ptr::null_mut(),
)
};
OwnedHandle::adopt(raw)
}
fn flush(handle: &OwnedHandle) -> io::Result<()> {
if unsafe { FlushFileBuffers(handle.raw()) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub(crate) fn flush_file(file: &std::fs::File) -> io::Result<()> {
use std::os::windows::io::AsRawHandle as _;
if unsafe { FlushFileBuffers(file.as_raw_handle().cast()) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub(crate) fn sync_path(path: &Path) -> io::Result<()> {
let is_directory = std::fs::metadata(path)?.is_dir();
let flags = if is_directory {
FILE_FLAG_BACKUP_SEMANTICS
} else {
FILE_ATTRIBUTE_NORMAL
};
let handle = open(
path,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
OPEN_EXISTING,
flags,
)?;
flush(&handle)
}
pub(crate) fn directory_identity(path: &Path) -> io::Result<(u64, u64)> {
let handle = open(
path,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
)?;
let mut info = BY_HANDLE_FILE_INFORMATION::default();
if unsafe { GetFileInformationByHandle(handle.raw(), &raw mut info) } == 0 {
return Err(io::Error::last_os_error());
}
let index = (u64::from(info.nFileIndexHigh) << 32) | u64::from(info.nFileIndexLow);
Ok((u64::from(info.dwVolumeSerialNumber), index))
}
pub(crate) const WRITER_LOCK_OFFSET: u64 = 0x4000_0000;
pub(crate) const WRITER_LOCK_RANGE: u64 = 1;
fn lock_file_range(file: &std::fs::File, flags: DWORD) -> io::Result<()> {
use std::os::windows::io::AsRawHandle as _;
let mut overlapped = OVERLAPPED {
Internal: 0,
InternalHigh: 0,
Offset: (WRITER_LOCK_OFFSET & 0xFFFF_FFFF) as DWORD,
OffsetHigh: (WRITER_LOCK_OFFSET >> 32) as DWORD,
hEvent: std::ptr::null_mut(),
};
let low = DWORD::try_from(WRITER_LOCK_RANGE)
.map_err(|_| io::Error::other("writer lock range exceeds DWORD"))?;
let ok = unsafe {
LockFileEx(
file.as_raw_handle().cast(),
flags,
0,
low,
0,
&raw mut overlapped,
)
};
if ok != 0 {
return Ok(());
}
let error = io::Error::last_os_error();
let code = error
.raw_os_error()
.and_then(|raw| DWORD::try_from(raw).ok());
match code {
Some(ERROR_LOCK_VIOLATION) => Err(io::Error::from(io::ErrorKind::WouldBlock)),
Some(ERROR_IO_PENDING) => Err(io::Error::other(
"LockFileEx returned ERROR_IO_PENDING despite LOCKFILE_FAIL_IMMEDIATELY",
)),
_ => Err(error),
}
}
pub(crate) fn lock_file_exclusive(file: &std::fs::File) -> io::Result<()> {
lock_file_range(file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY)
}
#[allow(dead_code)]
pub(crate) fn lock_file_shared(file: &std::fs::File) -> io::Result<()> {
lock_file_range(file, LOCKFILE_FAIL_IMMEDIATELY)
}
pub(crate) fn available_disk_bytes(path: &Path) -> io::Result<u64> {
let encoded = wide(path)?;
let mut available: u64 = 0;
let ok = unsafe {
GetDiskFreeSpaceExW(
encoded.as_ptr(),
&raw mut available,
std::ptr::null_mut(),
std::ptr::null_mut(),
)
};
if ok == 0 {
return Err(io::Error::last_os_error());
}
Ok(available)
}
fn page_size() -> io::Result<usize> {
let mut info = SYSTEM_INFO::default();
unsafe {
GetSystemInfo(&raw mut info);
}
let size = usize::try_from(info.dwPageSize)
.ok()
.filter(|value| *value != 0)
.ok_or_else(|| io::Error::other("GetSystemInfo reported an invalid page size"))?;
Ok(size)
}
pub fn resident_bytes(range: &[u8]) -> io::Result<u64> {
if range.is_empty() {
return Ok(0);
}
let page_size = page_size()?;
let start = range.as_ptr() as usize;
let end = start
.checked_add(range.len())
.ok_or_else(|| io::Error::other("mapped range end overflow"))?;
let aligned_start = start / page_size * page_size;
let aligned_end = end
.checked_add(page_size.saturating_sub(1))
.ok_or_else(|| io::Error::other("mapped range alignment overflow"))?
/ page_size
* page_size;
let page_count = aligned_end
.checked_sub(aligned_start)
.ok_or_else(|| io::Error::other("mapped range underflow"))?
/ page_size;
const QUERY_CAPACITY: usize = 1_024;
let mut block = [PSAPI_WORKING_SET_EX_INFORMATION {
VirtualAddress: std::ptr::null_mut(),
VirtualAttributes: 0,
}; QUERY_CAPACITY];
let process = unsafe { GetCurrentProcess() };
let mut first_page = 0_usize;
let mut resident = 0_u64;
while first_page < page_count {
let pages = QUERY_CAPACITY.min(page_count.saturating_sub(first_page));
let byte_offset = first_page
.checked_mul(page_size)
.ok_or_else(|| io::Error::other("working-set byte offset overflow"))?;
let chunk_start = aligned_start
.checked_add(byte_offset)
.ok_or_else(|| io::Error::other("working-set address overflow"))?;
for index in 0..pages {
let offset = index
.checked_mul(page_size)
.ok_or_else(|| io::Error::other("working-set page offset overflow"))?;
let address = chunk_start
.checked_add(offset)
.ok_or_else(|| io::Error::other("working-set page address overflow"))?;
let entry = block
.get_mut(index)
.ok_or_else(|| io::Error::other("working-set scratch index out of range"))?;
entry.VirtualAddress = address as *mut c_void;
entry.VirtualAttributes = 0;
}
let bytes = pages
.checked_mul(size_of::<PSAPI_WORKING_SET_EX_INFORMATION>())
.and_then(|value| DWORD::try_from(value).ok())
.ok_or_else(|| io::Error::other("working-set query size overflow"))?;
let ok = unsafe { K32QueryWorkingSetEx(process, block.as_mut_ptr().cast(), bytes) };
if ok == 0 {
return Err(io::Error::last_os_error());
}
for entry in block.iter().take(pages) {
if entry.VirtualAttributes & WORKING_SET_EX_VALID != WORKING_SET_EX_VALID {
continue;
}
let page_start = entry.VirtualAddress as usize;
let page_end = page_start
.checked_add(page_size)
.ok_or_else(|| io::Error::other("resident page end overflow"))?;
let overlap_start = page_start.max(start);
let overlap_end = page_end.min(end);
if overlap_end > overlap_start {
let overlap = u64::try_from(overlap_end.saturating_sub(overlap_start))
.map_err(|_| io::Error::other("resident overlap exceeds u64"))?;
resident = resident.saturating_add(overlap);
}
}
first_page = first_page.saturating_add(pages);
}
Ok(resident)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ProcessMemory {
pub working_set: u64,
pub private_bytes: u64,
}
pub fn process_memory() -> io::Result<ProcessMemory> {
let mut counters = PROCESS_MEMORY_COUNTERS_EX::default();
let size = DWORD::try_from(size_of::<PROCESS_MEMORY_COUNTERS_EX>())
.map_err(|_| io::Error::other("memory counter struct size exceeds DWORD"))?;
counters.cb = size;
let ok = unsafe { K32GetProcessMemoryInfo(GetCurrentProcess(), &raw mut counters, size) };
if ok == 0 {
return Err(io::Error::last_os_error());
}
Ok(ProcessMemory {
working_set: u64::try_from(counters.WorkingSetSize)
.map_err(|_| io::Error::other("working set exceeds u64"))?,
private_bytes: u64::try_from(counters.PrivateUsage)
.map_err(|_| io::Error::other("private bytes exceed u64"))?,
})
}
#[derive(Debug)]
pub(crate) struct FileMapping {
address: *mut c_void,
length: usize,
section: Option<OwnedHandle>,
file: Option<std::fs::File>,
}
unsafe impl Send for FileMapping {}
unsafe impl Sync for FileMapping {}
impl FileMapping {
pub(crate) fn from_file(file: std::fs::File, expected_length: u64) -> io::Result<Self> {
use std::os::windows::io::AsRawHandle as _;
if expected_length == 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"refusing to map an empty segment file",
));
}
let length = usize::try_from(expected_length).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"mapped file length exceeds usize",
)
})?;
let section_raw = unsafe {
CreateFileMappingW(
file.as_raw_handle().cast(),
std::ptr::null_mut(),
PAGE_WRITECOPY,
0,
0,
std::ptr::null(),
)
};
let section = OwnedHandle::adopt(section_raw)?;
let address = unsafe { MapViewOfFile(section.raw(), FILE_MAP_READ, 0, 0, length) };
if address.is_null() {
let error = io::Error::last_os_error();
drop(section);
drop(file);
return Err(error);
}
Ok(Self {
address,
length,
section: Some(section),
file: Some(file),
})
}
pub(crate) fn as_bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.address.cast::<u8>(), self.length) }
}
pub(crate) const fn length(&self) -> usize {
self.length
}
#[cfg(any(test, feature = "test-seams"))]
pub(crate) fn corrupt_byte(&mut self, offset: usize, mask: u8) -> io::Result<()> {
if offset >= self.length {
return Err(io::Error::other("corruption offset is outside the mapping"));
}
let page_size = page_size()?;
let page_offset = offset - offset % page_size;
let page = self.address.cast::<u8>().wrapping_add(page_offset).cast();
let mut previous: DWORD = 0;
if unsafe { VirtualProtect(page, page_size, PAGE_WRITECOPY, &raw mut previous) } == 0 {
return Err(io::Error::last_os_error());
}
unsafe {
let byte = self.address.cast::<u8>().add(offset);
byte.write(byte.read() ^ mask);
}
let mut restored: DWORD = 0;
if unsafe { VirtualProtect(page, page_size, PAGE_READONLY, &raw mut restored) } == 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub(crate) fn resident_bytes(&self) -> io::Result<u64> {
resident_bytes(self.as_bytes())
}
}
impl Drop for FileMapping {
fn drop(&mut self) {
unsafe {
UnmapViewOfFile(self.address);
}
drop(self.section.take());
drop(self.file.take());
}
}
#[cfg(any(test, feature = "test-seams"))]
#[derive(Debug)]
pub struct TestMapping(FileMapping);
#[cfg(any(test, feature = "test-seams"))]
impl TestMapping {
pub fn open(file: std::fs::File, length: u64) -> io::Result<Self> {
FileMapping::from_file(file, length).map(Self)
}
#[must_use]
pub fn bytes(&self) -> &[u8] {
self.0.as_bytes()
}
#[must_use]
pub const fn length(&self) -> usize {
self.0.length()
}
pub fn resident_bytes(&self) -> io::Result<u64> {
self.0.resident_bytes()
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::{
FileMapping, PROCESS_MEMORY_COUNTERS_EX, PSAPI_WORKING_SET_EX_INFORMATION,
available_disk_bytes, page_size, process_memory, resident_bytes, sync_path, wide,
};
use std::path::Path;
#[test]
fn constants_match_the_installed_sdk() {
assert_eq!(super::GENERIC_WRITE, 0x4000_0000);
assert_eq!(super::FILE_SHARE_READ, 1);
assert_eq!(super::FILE_SHARE_WRITE, 2);
assert_eq!(super::FILE_SHARE_DELETE, 4);
assert_eq!(super::FILE_ATTRIBUTE_NORMAL, 0x80);
assert_eq!(super::FILE_FLAG_BACKUP_SEMANTICS, 0x0200_0000);
assert_eq!(super::PAGE_READONLY, 0x02);
assert_eq!(super::PAGE_WRITECOPY, 0x08);
assert_eq!(super::FILE_MAP_READ, 0x0004);
assert_eq!(super::OPEN_EXISTING, 3);
assert_eq!(super::LOCKFILE_FAIL_IMMEDIATELY, 1);
assert_eq!(super::LOCKFILE_EXCLUSIVE_LOCK, 2);
assert_eq!(super::ERROR_ACCESS_DENIED, 5);
assert_eq!(super::ERROR_LOCK_VIOLATION, 33);
assert_eq!(super::INVALID_HANDLE_VALUE as isize, -1);
}
#[test]
fn declared_struct_layouts_match_the_sdk() {
assert_eq!(
size_of::<PSAPI_WORKING_SET_EX_INFORMATION>(),
2 * size_of::<usize>()
);
assert_eq!(
size_of::<PROCESS_MEMORY_COUNTERS_EX>(),
size_of::<usize>() + 9 * size_of::<usize>()
);
}
#[test]
fn an_interior_nul_is_rejected_before_any_kernel_call() {
let error = wide(Path::new("bad\u{0}path")).expect_err("interior NUL");
assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
}
#[test]
fn the_page_size_is_a_non_zero_power_of_two() {
let size = page_size().expect("page size");
assert!(size.is_power_of_two());
assert!(size >= 4_096);
}
#[test]
fn process_memory_reports_non_zero_windows_figures() {
let memory = process_memory().expect("process memory");
assert!(memory.working_set > 0, "a live process has a working set");
assert!(memory.private_bytes > 0, "a live process has private bytes");
}
#[test]
fn free_space_is_reported_for_the_temp_volume() {
let available = available_disk_bytes(&std::env::temp_dir()).expect("free space");
assert!(available > 0);
}
#[test]
fn residency_of_an_empty_range_is_zero_without_a_kernel_call() {
assert_eq!(resident_bytes(&[]).expect("empty range"), 0);
}
#[test]
fn a_directory_is_synchronised_through_a_writable_backup_semantics_handle() {
let directory = std::env::temp_dir().join(format!(
"ze-sys-windows-sync-{}-{}",
std::process::id(),
line!()
));
std::fs::create_dir_all(&directory).expect("create");
std::fs::write(directory.join("artifact"), b"bytes").expect("write");
sync_path(&directory.join("artifact")).expect("file sync");
sync_path(&directory).expect("directory sync");
std::fs::remove_dir_all(&directory).expect("cleanup");
}
fn scratch(label: &str) -> std::path::PathBuf {
let directory = std::env::temp_dir().join(format!(
"ze-sys-windows-{label}-{}-{:?}",
std::process::id(),
std::thread::current().id()
));
std::fs::create_dir_all(&directory).expect("create scratch");
directory
}
#[test]
fn an_append_handle_can_be_flushed() {
let directory = scratch("append-flush");
let path = directory.join("wal.zwal");
let mut file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&path)
.expect("open append");
std::io::Write::write_all(&mut file, b"record-one\n").expect("append");
super::flush_file(&file).expect("an append handle must be flushable");
std::io::Write::write_all(&mut file, b"record-two\n").expect("append again");
super::flush_file(&file).expect("second flush");
drop(file);
assert_eq!(
std::fs::read(&path).expect("read"),
b"record-one\nrecord-two\n"
);
std::fs::remove_dir_all(&directory).expect("cleanup");
}
#[test]
fn a_read_only_handle_is_refused_by_the_flush() {
let directory = scratch("readonly-flush");
let path = directory.join("artifact");
std::fs::write(&path, b"bytes").expect("seed");
let file = std::fs::File::open(&path).expect("read-only open");
let error = super::flush_file(&file).expect_err("a read-only handle must not flush");
assert_eq!(
error.raw_os_error().and_then(|raw| u32::try_from(raw).ok()),
Some(super::ERROR_ACCESS_DENIED)
);
drop(file);
std::fs::remove_dir_all(&directory).expect("cleanup");
}
#[test]
fn path_aliases_resolve_to_one_store_identity() {
let directory = scratch("identity");
let nested = directory.join("store");
std::fs::create_dir_all(&nested).expect("nested");
let canonical = super::directory_identity(&nested).expect("identity");
let slashed = std::path::PathBuf::from(nested.to_string_lossy().replace('\\', "/"));
assert_eq!(
super::directory_identity(&slashed).expect("slashed"),
canonical
);
let dotted = nested.join(".");
assert_eq!(
super::directory_identity(&dotted).expect("dotted"),
canonical
);
let uppercased = std::path::PathBuf::from(nested.to_string_lossy().to_uppercase());
assert_eq!(
super::directory_identity(&uppercased).expect("uppercased"),
canonical
);
let sibling = directory.join("other");
std::fs::create_dir_all(&sibling).expect("sibling");
assert_ne!(
super::directory_identity(&sibling).expect("sibling identity"),
canonical
);
std::fs::remove_dir_all(&directory).expect("cleanup");
}
#[test]
fn mapping_reports_length_and_residency_separately() {
let directory = scratch("map");
let path = directory.join("segment.zseg");
let payload: Vec<u8> = (0..65_536_u32).map(|value| (value % 251) as u8).collect();
std::fs::write(&path, &payload).expect("seed");
let file = std::fs::File::open(&path).expect("open");
let mapping = FileMapping::from_file(file, payload.len() as u64).expect("map");
assert_eq!(mapping.length(), payload.len());
assert_eq!(mapping.as_bytes(), payload.as_slice());
let resident = mapping.resident_bytes().expect("residency");
assert!(
resident <= mapping.length() as u64,
"residency {resident} must never exceed the mapped length {}",
mapping.length()
);
drop(mapping);
std::fs::remove_dir_all(&directory).expect("cleanup");
}
#[test]
fn a_mapped_artifact_can_be_unlinked_while_its_reader_stays_valid() {
let directory = scratch("unlink");
let path = directory.join("segment.zseg");
let payload: Vec<u8> = (0..8_192_u32).map(|value| (value % 251) as u8).collect();
std::fs::write(&path, &payload).expect("seed");
let file = std::fs::File::open(&path).expect("open");
let mapping = FileMapping::from_file(file, payload.len() as u64).expect("map");
std::fs::remove_file(&path).expect("a std::fs::File shares deletion by default");
assert!(
!path.try_exists().expect("exists probe"),
"unlinked at once"
);
assert_eq!(
mapping.as_bytes(),
payload.as_slice(),
"a retained reader keeps its validated bytes after the unlink"
);
drop(mapping);
std::fs::remove_dir_all(&directory).expect("cleanup");
}
#[test]
fn corrupting_a_mapped_byte_does_not_reach_the_file() {
let directory = scratch("corrupt");
let path = directory.join("segment.zseg");
let payload = vec![0xAA_u8; 8_192];
std::fs::write(&path, &payload).expect("seed");
let file = std::fs::File::open(&path).expect("open");
let mut mapping = FileMapping::from_file(file, payload.len() as u64).expect("map");
mapping.corrupt_byte(4_100, 0xFF).expect("corrupt");
assert_eq!(mapping.as_bytes().get(4_100).copied(), Some(0x55));
assert_eq!(mapping.as_bytes().get(4_099).copied(), Some(0xAA));
assert_eq!(
std::fs::read(&path).expect("read").get(4_100).copied(),
Some(0xAA),
"copy-on-write must leave the file untouched"
);
assert!(mapping.corrupt_byte(payload.len(), 0x01).is_err());
drop(mapping);
std::fs::remove_dir_all(&directory).expect("cleanup");
}
#[test]
fn an_empty_file_is_refused_with_a_typed_error() {
let directory = scratch("empty");
let path = directory.join("empty.zseg");
std::fs::write(&path, b"").expect("seed");
let file = std::fs::File::open(&path).expect("open");
let error = FileMapping::from_file(file, 0).expect_err("empty");
assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
std::fs::remove_dir_all(&directory).expect("cleanup");
}
#[test]
fn a_length_beyond_the_file_is_refused() {
let directory = scratch("truncated");
let path = directory.join("short.zseg");
std::fs::write(&path, b"only-sixteen-b!!").expect("seed");
let file = std::fs::File::open(&path).expect("open");
let error = FileMapping::from_file(file, 1 << 20).expect_err("over-long mapping");
assert!(
error.raw_os_error().is_some(),
"a real Win32 error: {error}"
);
std::fs::remove_dir_all(&directory).expect("cleanup");
}
}
#[cfg(all(test, windows))]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod lock_tests {
use super::{lock_file_exclusive, lock_file_shared};
use std::fs::{File, OpenOptions};
use std::io;
use std::path::PathBuf;
fn lock_scratch(label: &str) -> PathBuf {
let directory = std::env::temp_dir().join(format!(
"ze-sys-windows-lock-{label}-{}-{:?}",
std::process::id(),
std::thread::current().id()
));
std::fs::create_dir_all(&directory).expect("create lock scratch");
let path = directory.join("writer.lock");
std::fs::write(&path, b"").expect("seed lock file");
path
}
fn open_reader(path: &std::path::Path) -> File {
File::open(path).expect("open read handle")
}
fn open_writer(path: &std::path::Path) -> File {
OpenOptions::new()
.read(true)
.write(true)
.open(path)
.expect("open write handle")
}
fn contention(error: &io::Error) -> bool {
error.kind() == io::ErrorKind::WouldBlock
}
#[test]
fn two_shared_holders_exclude_a_writer_until_both_release() {
let path = lock_scratch("shared-pair");
let first = open_reader(&path);
lock_file_shared(&first).expect("first shared holder");
let second = open_reader(&path);
lock_file_shared(&second).expect("second shared holder beside the first");
let writer = open_writer(&path);
let blocked = lock_file_exclusive(&writer).expect_err("two readers exclude a writer");
assert!(contention(&blocked), "expected WouldBlock, got {blocked}");
drop(first);
let still_blocked =
lock_file_exclusive(&writer).expect_err("one surviving reader still excludes a writer");
assert!(
contention(&still_blocked),
"expected WouldBlock, got {still_blocked}"
);
drop(second);
lock_file_exclusive(&writer).expect("the last reader's release admits the writer");
drop(writer);
let _ = std::fs::remove_dir_all(path.parent().expect("scratch parent"));
}
#[test]
fn an_exclusive_holder_excludes_a_shared_holder() {
let path = lock_scratch("exclusive-first");
let writer = open_writer(&path);
lock_file_exclusive(&writer).expect("exclusive holder");
let reader = open_reader(&path);
let blocked = lock_file_shared(&reader).expect_err("a writer excludes a reader");
assert!(contention(&blocked), "expected WouldBlock, got {blocked}");
drop(writer);
lock_file_shared(&reader).expect("the writer's release admits the reader");
drop(reader);
let _ = std::fs::remove_dir_all(path.parent().expect("scratch parent"));
}
}
#[cfg(all(test, windows))]
#[allow(clippy::expect_used)]
mod entropy_tests {
#[test]
fn system_preferred_rng_fills_sixteen_bytes_and_two_calls_differ() {
let mut first = [0; 16];
let mut second = [0; 16];
super::fill_entropy(&mut first).expect("system entropy");
super::fill_entropy(&mut second).expect("system entropy");
assert_ne!(first, [0; 16]);
assert_ne!(second, [0; 16]);
assert_ne!(first, second);
}
}