use super::*;
impl File {
pub(crate) fn rollback_wal_append(
&self,
committed_bytes: u64,
empty_header: &[u8],
) -> io::Result<u64> {
self.writable_native()?;
#[cfg(windows)]
let rollback = self.reopen_wal_for_rollback()?;
#[cfg(not(windows))]
let rollback = self;
if rollback.metadata()?.len() < committed_bytes {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"WAL lost previously appended bytes before rollback",
));
}
let restored_bytes = if committed_bytes == 0 {
let length = empty_header.len() as u64;
let present = rollback.metadata()?.len();
if present < length {
let mut writer = self;
writer.write_all(&empty_header[present as usize..])?;
}
length
} else {
committed_bytes
};
rollback.set_len(restored_bytes)?;
rollback.sync_all()?;
Ok(restored_bytes)
}
#[cfg(windows)]
fn reopen_wal_for_rollback(&self) -> io::Result<Self> {
use std::ffi::c_void;
use std::os::windows::io::{AsRawHandle, FromRawHandle};
#[link(name = "kernel32")]
unsafe extern "system" {
#[link_name = "ReOpenFile"]
fn reopen_file(
original: *mut c_void,
desired_access: u32,
share_mode: u32,
flags: u32,
) -> *mut c_void;
}
let FileBacking::Native(file) = &self.backing else {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"WAL rollback requires a native append handle",
));
};
let permit = file.permit.context.acquire(file.permit.kind())?;
let raw = unsafe { reopen_file(file.inner.as_raw_handle(), 0x4000_0000, 7, 0) };
if raw == -1isize as *mut c_void {
return Err(file
.permit
.context
.map_open_error(io::Error::last_os_error()));
}
let inner = unsafe { std::fs::File::from_raw_handle(raw) };
if crate::native_file_identity::of_file(&inner)?
!= crate::native_file_identity::of_file(&file.inner)?
{
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"WAL rollback handle does not name the original append object",
));
}
Ok(Self {
backing: FileBacking::Native(NativeFile {
inner,
permit,
#[cfg(any(test, feature = "test-support"))]
trace: file.trace.clone(),
}),
})
}
}