#![forbid(unsafe_code)]
use std::fs::File;
use std::io;
use std::path::Path;
#[cfg(unix)]
pub(crate) fn sync_dir(path: &Path) -> io::Result<()> {
File::open(path)?.sync_all()
}
#[cfg(windows)]
pub(crate) fn sync_dir(path: &Path) -> io::Result<()> {
use std::os::windows::fs::OpenOptionsExt;
const FILE_FLAG_BACKUP_SEMANTICS: u32 = 0x0200_0000;
let dir = std::fs::OpenOptions::new()
.read(true)
.custom_flags(FILE_FLAG_BACKUP_SEMANTICS)
.open(path)?;
match dir.sync_all() {
Ok(()) => Ok(()),
Err(e)
if matches!(
e.kind(),
io::ErrorKind::PermissionDenied | io::ErrorKind::Unsupported
) =>
{
Ok(())
}
Err(e) => Err(e),
}
}
#[cfg(unix)]
pub(crate) fn read_at(
file: &File,
buf: &mut [u8],
offset: u64,
) -> io::Result<usize> {
use std::os::unix::fs::FileExt;
file.read_at(buf, offset)
}
#[cfg(windows)]
pub(crate) fn read_at(
file: &File,
buf: &mut [u8],
offset: u64,
) -> io::Result<usize> {
use std::os::windows::fs::FileExt;
file.seek_read(buf, offset)
}
#[cfg(unix)]
pub(crate) fn read_exact_at(
file: &File,
buf: &mut [u8],
offset: u64,
) -> io::Result<()> {
use std::os::unix::fs::FileExt;
file.read_exact_at(buf, offset)
}
#[cfg(windows)]
pub(crate) fn read_exact_at(
file: &File,
mut buf: &mut [u8],
mut offset: u64,
) -> io::Result<()> {
use std::os::windows::fs::FileExt;
while !buf.is_empty() {
match file.seek_read(buf, offset) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
offset += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
if buf.is_empty() {
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
}
}
#[cfg(unix)]
pub(crate) fn write_all_at(
file: &File,
buf: &[u8],
offset: u64,
) -> io::Result<()> {
use std::os::unix::fs::FileExt;
match crate::faultdisk::on_write(buf.len()) {
crate::faultdisk::WriteFault::None => {}
crate::faultdisk::WriteFault::DiskFull => {
return Err(io::Error::new(
io::ErrorKind::StorageFull,
"faultdisk: simulated ENOSPC",
));
}
crate::faultdisk::WriteFault::Torn(keep) => {
file.write_all_at(&buf[..keep], offset)?;
crate::faultdisk::power_cut();
}
crate::faultdisk::WriteFault::Corrupt { offset_in_buf, len } => {
file.write_all_at(buf, offset)?;
let mut flipped = buf[offset_in_buf..offset_in_buf + len].to_vec();
for b in &mut flipped {
*b = !*b;
}
file.write_all_at(&flipped, offset + offset_in_buf as u64)?;
return Ok(());
}
}
file.write_all_at(buf, offset)
}
#[cfg(windows)]
pub(crate) fn write_all_at(
file: &File,
buf: &[u8],
offset: u64,
) -> io::Result<()> {
use std::os::windows::fs::FileExt;
match crate::faultdisk::on_write(buf.len()) {
crate::faultdisk::WriteFault::None => {}
crate::faultdisk::WriteFault::DiskFull => {
return Err(io::Error::new(
io::ErrorKind::StorageFull,
"faultdisk: simulated ENOSPC",
));
}
crate::faultdisk::WriteFault::Torn(keep) => {
win_write_all(file, &buf[..keep], offset)?;
crate::faultdisk::power_cut();
}
crate::faultdisk::WriteFault::Corrupt { offset_in_buf, len } => {
win_write_all(file, buf, offset)?;
let mut flipped = buf[offset_in_buf..offset_in_buf + len].to_vec();
for b in &mut flipped {
*b = !*b;
}
return win_write_all(
file,
&flipped,
offset + offset_in_buf as u64,
);
}
}
win_write_all(file, buf, offset)
}
#[cfg(windows)]
fn win_write_all(
file: &File,
mut buf: &[u8],
mut offset: u64,
) -> io::Result<()> {
use std::os::windows::fs::FileExt;
while !buf.is_empty() {
match file.seek_write(buf, offset) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to write whole buffer",
));
}
Ok(n) => {
buf = &buf[n..];
offset += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}