use std::fs::File;
use std::io;
use std::path::Path;
pub(crate) fn open(path: &Path, direct: bool, create: bool) -> io::Result<File> {
imp::open(path, direct, create)
}
pub(crate) fn sync_data(file: &File) -> io::Result<()> {
imp::sync_data(file)
}
pub(crate) fn read_at_full(file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
let mut read = 0;
while read < buf.len() {
match pread(file, &mut buf[read..], offset + read as u64) {
Ok(0) => break,
Ok(n) => read += n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(read)
}
pub(crate) fn write_all_at(file: &File, buf: &[u8], offset: u64) -> io::Result<()> {
let mut written = 0;
while written < buf.len() {
match pwrite(file, &buf[written..], offset + written as u64) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"positioned write returned zero",
));
}
Ok(n) => written += n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
#[cfg(unix)]
#[inline]
fn pread(file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
use std::os::unix::fs::FileExt;
file.read_at(buf, offset)
}
#[cfg(unix)]
#[inline]
fn pwrite(file: &File, buf: &[u8], offset: u64) -> io::Result<usize> {
use std::os::unix::fs::FileExt;
file.write_at(buf, offset)
}
#[cfg(windows)]
#[inline]
fn pread(file: &File, buf: &mut [u8], offset: u64) -> io::Result<usize> {
use std::os::windows::fs::FileExt;
file.seek_read(buf, offset)
}
#[cfg(windows)]
#[inline]
fn pwrite(file: &File, buf: &[u8], offset: u64) -> io::Result<usize> {
use std::os::windows::fs::FileExt;
file.seek_write(buf, offset)
}
#[cfg(target_os = "linux")]
mod imp {
use std::fs::{File, OpenOptions};
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::path::Path;
pub(super) fn open(path: &Path, direct: bool, create: bool) -> io::Result<File> {
let mut opts = OpenOptions::new();
let _ = opts.read(true).write(true).create(create);
if direct {
let _ = opts.custom_flags(libc::O_DIRECT);
}
opts.open(path)
}
pub(super) fn sync_data(file: &File) -> io::Result<()> {
file.sync_data()
}
}
#[cfg(target_os = "macos")]
mod imp {
use std::fs::{File, OpenOptions};
use std::io;
use std::os::unix::io::AsRawFd;
use std::path::Path;
pub(super) fn open(path: &Path, direct: bool, create: bool) -> io::Result<File> {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(create)
.open(path)?;
if direct {
let fd = file.as_raw_fd();
let rc = unsafe { libc::fcntl(fd, libc::F_NOCACHE, 1) };
if rc == -1 {
return Err(io::Error::last_os_error());
}
}
Ok(file)
}
pub(super) fn sync_data(file: &File) -> io::Result<()> {
let fd = file.as_raw_fd();
let rc = unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) };
if rc == -1 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
#[cfg(windows)]
mod imp {
use std::fs::{File, OpenOptions};
use std::io;
use std::os::windows::fs::OpenOptionsExt;
use std::path::Path;
const FILE_FLAG_WRITE_THROUGH: u32 = 0x8000_0000;
const FILE_FLAG_NO_BUFFERING: u32 = 0x2000_0000;
pub(super) fn open(path: &Path, direct: bool, create: bool) -> io::Result<File> {
let mut opts = OpenOptions::new();
let _ = opts.read(true).write(true).create(create);
if direct {
let _ = opts.custom_flags(FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH);
}
opts.open(path)
}
pub(super) fn sync_data(file: &File) -> io::Result<()> {
file.sync_data()
}
}
#[cfg(all(unix, not(target_os = "linux"), not(target_os = "macos")))]
mod imp {
use std::fs::{File, OpenOptions};
use std::io;
use std::path::Path;
pub(super) fn open(path: &Path, _direct: bool, create: bool) -> io::Result<File> {
OpenOptions::new()
.read(true)
.write(true)
.create(create)
.open(path)
}
pub(super) fn sync_data(file: &File) -> io::Result<()> {
file.sync_data()
}
}