use std::fs::{File, OpenOptions};
use std::path::Path;
use crate::error::{DbError, DbResult};
use crate::io::aligned_buf::AlignedBuf;
#[cfg(target_os = "macos")]
unsafe extern "C" {
fn fcntl(fd: std::os::raw::c_int, cmd: std::os::raw::c_int, ...) -> std::os::raw::c_int;
}
pub fn pread_value(file: &File, offset: u64, len: usize) -> DbResult<Vec<u8>> {
use std::os::unix::fs::FileExt;
let sector_size = 4096;
let aligned_offset = offset & !(sector_size - 1);
let diff = (offset - aligned_offset) as usize;
let aligned_len = (diff + len + sector_size as usize - 1) & !(sector_size as usize - 1);
let mut buf = AlignedBuf::zeroed(aligned_len);
let mut total_read = 0;
while total_read < diff + len {
let r = file.read_at(&mut buf[total_read..], aligned_offset + total_read as u64)?;
if r == 0 {
break; }
total_read += r;
}
if total_read < diff + len {
return Err(DbError::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"failed to read full value",
)));
}
let mut result = vec![0u8; len];
result.copy_from_slice(&buf[diff..diff + len]);
Ok(result)
}
#[cfg(feature = "var-collections")]
pub fn pread_block(file: &File, block_offset: u64) -> DbResult<(AlignedBuf, usize)> {
use std::os::unix::fs::FileExt;
debug_assert!(
block_offset & 4095 == 0,
"block_offset must be 4096-aligned"
);
let mut buf = AlignedBuf::zeroed(4096);
let n = file.read_at(&mut buf, block_offset)?;
Ok((buf, n))
}
pub fn open_read(path: &Path) -> DbResult<File> {
let file = OpenOptions::new().read(true).open(path)?;
#[cfg(target_os = "macos")]
{
use std::os::unix::io::AsRawFd;
unsafe {
fcntl(file.as_raw_fd(), 48 , 1);
}
}
Ok(file)
}
pub fn open_write(path: &Path) -> DbResult<File> {
let file = OpenOptions::new()
.create(true)
.read(true)
.write(true)
.truncate(false)
.open(path)?;
#[cfg(target_os = "macos")]
{
use std::os::unix::io::AsRawFd;
unsafe {
fcntl(file.as_raw_fd(), 48 , 1);
}
}
Ok(file)
}
pub fn pwrite_at(file: &File, data: &[u8], offset: u64) -> DbResult<()> {
use std::os::unix::fs::FileExt;
file.write_all_at(data, offset)?;
Ok(())
}
pub fn fsync(file: &File) -> DbResult<()> {
file.sync_data()?;
Ok(())
}
#[cfg(feature = "encryption")]
pub fn pread_value_encrypted(
file: &File,
tag_file: &crate::io::tags::TagFile,
cipher: &crate::crypto::PageCipher,
file_id: u32,
offset: u64,
len: usize,
) -> DbResult<Vec<u8>> {
use std::os::unix::fs::FileExt;
let sector_size: u64 = 4096;
let aligned_offset = offset & !(sector_size - 1);
let diff = (offset - aligned_offset) as usize;
let aligned_len = (diff + len + sector_size as usize - 1) & !(sector_size as usize - 1);
let mut buf = AlignedBuf::zeroed(aligned_len);
let mut total_read = 0;
while total_read < diff + len {
let r = file.read_at(&mut buf[total_read..], aligned_offset + total_read as u64)?;
if r == 0 {
break;
}
total_read += r;
}
if total_read < diff + len {
return Err(DbError::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"failed to read full value",
)));
}
let start_page = aligned_offset / sector_size;
let num_pages = aligned_len / sector_size as usize;
let tags = tag_file.read_tags(start_page, num_pages)?;
#[allow(clippy::needless_range_loop)]
for i in 0..num_pages {
let page_start = i * sector_size as usize;
let page = &mut buf[page_start..page_start + sector_size as usize];
cipher.decrypt_page(file_id, start_page + i as u64, page, &tags[i])?;
}
let mut result = vec![0u8; len];
result.copy_from_slice(&buf[diff..diff + len]);
Ok(result)
}