Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/memvfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct MemVfs {

impl Vfs for MemVfs {
type Handle = File;
// unused
type Region = [u8; 0];

fn open(&self, path: Option<&str>, opts: OpenOpts) -> VfsResult<Self::Handle> {
log::debug!("open: path={:?}, opts={:?}", path, opts);
Expand Down
26 changes: 26 additions & 0 deletions src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,29 @@ impl From<i32> for LockLevel {
}
}
}

#[derive(Copy, Clone, Debug)]
pub enum ShmLockMode {
LockShared,
LockExclusive,
UnlockShared,
UnlockExclusive,
}

impl TryFrom<i32> for ShmLockMode {
type Error = i32;

fn try_from(flags: i32) -> Result<Self, Self::Error> {
const LOCK_SHARED: i32 = vars::SQLITE_SHM_LOCK | vars::SQLITE_SHM_SHARED;
const LOCK_EXCLUSIVE: i32 = vars::SQLITE_SHM_LOCK | vars::SQLITE_SHM_EXCLUSIVE;
const UNLOCK_SHARED: i32 = vars::SQLITE_SHM_UNLOCK | vars::SQLITE_SHM_SHARED;
const UNLOCK_EXCLUSIVE: i32 = vars::SQLITE_SHM_UNLOCK | vars::SQLITE_SHM_EXCLUSIVE;
Ok(match flags {
LOCK_SHARED => Self::LockShared,
LOCK_EXCLUSIVE => Self::LockExclusive,
UNLOCK_SHARED => Self::UnlockShared,
UNLOCK_EXCLUSIVE => Self::UnlockExclusive,
_ => return Err(vars::SQLITE_IOERR),
})
}
}
2 changes: 2 additions & 0 deletions src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ impl MockState {
impl Vfs for MockVfs {
// a simple usize that represents a file handle.
type Handle = MockHandle;
// unused
type Region = [u8; 0];

fn canonical_path<'a>(&self, path: Cow<'a, str>) -> VfsResult<Cow<'a, str>> {
let mut state = self.state();
Expand Down
168 changes: 138 additions & 30 deletions src/vfs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::flags::{AccessFlags, LockLevel, OpenOpts};
use crate::flags::{AccessFlags, LockLevel, OpenOpts, ShmLockMode};
use crate::logger::SqliteLogger;
use crate::vars::SQLITE_ERROR;
use crate::{ffi, vars};
Expand All @@ -7,7 +7,8 @@ use alloc::boxed::Box;
use alloc::ffi::CString;
use alloc::format;
use alloc::string::String;
use core::mem::{self, ManuallyDrop, MaybeUninit, size_of};
use alloc::vec::Vec;
use core::mem::{ManuallyDrop, size_of};
use core::slice;
use core::{
ffi::{CStr, c_char, c_int, c_void},
Expand Down Expand Up @@ -42,10 +43,12 @@ pub type VfsResult<T> = Result<T, SqliteErr>;
// FileWrapper needs to be repr(C) and have sqlite3_file as it's first member
// because it's a "subclass" of sqlite3_file
#[repr(C)]
struct FileWrapper<Handle> {
struct FileWrapper<T: Vfs> {
file: ffi::sqlite3_file,
vfs: *mut ffi::sqlite3_vfs,
handle: MaybeUninit<Handle>,
handle: T::Handle,
// Mapped region handles
mapped: Vec<Box<T::Region>>,
}

struct AppData<Vfs> {
Expand Down Expand Up @@ -124,8 +127,8 @@ macro_rules! unwrap_base_vfs {
macro_rules! unwrap_file {
($p_file:expr, $t_vfs:ty) => {
unsafe {
let out: VfsResult<&mut FileWrapper<<$t_vfs>::Handle>> = $p_file
.cast::<FileWrapper<<$t_vfs>::Handle>>()
let out: VfsResult<&mut FileWrapper<$t_vfs>> = $p_file
.cast::<FileWrapper<$t_vfs>>()
.as_mut()
.ok_or(vars::SQLITE_INTERNAL);
out
Expand All @@ -141,6 +144,7 @@ pub trait VfsHandle: Send {
#[allow(unused_variables)]
pub trait Vfs: Send + Sync {
type Handle: VfsHandle;
type Region: AsMut<[u8]> + Send;

/// construct a canonical version of the given path
fn canonical_path<'a>(&self, path: Cow<'a, str>) -> VfsResult<Cow<'a, str>> {
Expand Down Expand Up @@ -186,6 +190,32 @@ pub trait Vfs: Send + Sync {
fn device_characteristics(&self, handle: &mut Self::Handle) -> VfsResult<i32> {
Ok(DEFAULT_DEVICE_CHARACTERISTICS)
}

fn shm_map(
&self,
handle: &mut Self::Handle,
region_idx: usize,
region_size: usize,
extend: bool,
) -> VfsResult<Option<Self::Region>> {
Err(vars::SQLITE_READONLY_CANTINIT)
}

fn shm_lock(
&self,
handle: &mut Self::Handle,
offset: u32,
count: u32,
mode: ShmLockMode,
) -> VfsResult<()> {
Err(vars::SQLITE_IOERR)
}

fn shm_barrier(&self, handle: &mut Self::Handle) {}

fn shm_unmap(&self, handle: &mut Self::Handle, delete: bool) -> VfsResult<()> {
Err(vars::SQLITE_IOERR)
}
}

#[derive(Clone)]
Expand Down Expand Up @@ -300,10 +330,10 @@ fn register_inner<T: Vfs>(
xFileControl: Some(x_file_control::<T>),
xSectorSize: Some(x_sector_size::<T>),
xDeviceCharacteristics: Some(x_device_characteristics::<T>),
xShmMap: None,
xShmLock: None,
xShmBarrier: None,
xShmUnmap: None,
xShmMap: Some(x_shm_map::<T>),
xShmLock: Some(x_shm_lock::<T>),
xShmBarrier: Some(x_shm_barrier::<T>),
xShmUnmap: Some(x_shm_unmap::<T>),
xFetch: None,
xUnfetch: None,
};
Expand All @@ -315,7 +345,7 @@ fn register_inner<T: Vfs>(
let vfs_register = sqlite_api.register;
let p_appdata = Box::into_raw(Box::new(AppData { base_vfs, vfs, io_methods, sqlite_api }));

let filewrapper_size: c_int = size_of::<FileWrapper<T::Handle>>()
let filewrapper_size: c_int = size_of::<FileWrapper<T>>()
.try_into()
.map_err(|_| vars::SQLITE_INTERNAL)?;

Expand Down Expand Up @@ -371,7 +401,6 @@ unsafe extern "C" fn x_open<T: Vfs>(
let vfs = unwrap_vfs!(p_vfs, T)?;
let handle = vfs.open(name.as_ref().map(|s| s.as_ref()), opts)?;

let out_file = unwrap_file!(p_file, T)?;
let appdata = unwrap_appdata!(p_vfs, T)?;

if let Some(p_out_flags) = unsafe { p_out_flags.as_mut() } {
Expand All @@ -385,9 +414,18 @@ unsafe extern "C" fn x_open<T: Vfs>(
*p_out_flags = out_flags;
}

out_file.file.pMethods = &appdata.io_methods;
out_file.vfs = p_vfs;
out_file.handle.write(handle);
let out_file = p_file.cast::<FileWrapper<T>>();
unsafe {
core::ptr::write(
out_file,
FileWrapper {
file: ffi::sqlite3_file { pMethods: &appdata.io_methods },
vfs: p_vfs,
handle,
mapped: Vec::new(),
},
);
}

Ok(vars::SQLITE_OK)
})
Expand Down Expand Up @@ -447,11 +485,9 @@ unsafe extern "C" fn x_full_pathname<T: Vfs>(

unsafe extern "C" fn x_close<T: Vfs>(p_file: *mut ffi::sqlite3_file) -> c_int {
fallible(|| {
let file = unwrap_file!(p_file, T)?;
let file = unsafe { core::ptr::read(p_file.cast::<FileWrapper<T>>()) };
let vfs = unwrap_vfs!(file.vfs, T)?;
let handle = mem::replace(&mut file.handle, MaybeUninit::uninit());
let handle = unsafe { handle.assume_init() };
vfs.close(handle)?;
vfs.close(file.handle)?;
Ok(vars::SQLITE_OK)
})
}
Expand All @@ -468,7 +504,7 @@ unsafe extern "C" fn x_read<T: Vfs>(
let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?;
let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_READ)?;
let buf = unsafe { slice::from_raw_parts_mut(buf.cast::<u8>(), buf_len) };
vfs.read(unsafe { file.handle.assume_init_mut() }, offset, buf)?;
vfs.read(&mut file.handle, offset, buf)?;
Ok(vars::SQLITE_OK)
})
}
Expand All @@ -485,7 +521,7 @@ unsafe extern "C" fn x_write<T: Vfs>(
let buf_len: usize = i_amt.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?;
let offset: usize = i_ofst.try_into().map_err(|_| vars::SQLITE_IOERR_WRITE)?;
let buf = unsafe { slice::from_raw_parts(buf.cast::<u8>(), buf_len) };
let n = vfs.write(unsafe { file.handle.assume_init_mut() }, offset, buf)?;
let n = vfs.write(&mut file.handle, offset, buf)?;
if n != buf_len {
return Err(vars::SQLITE_IOERR_WRITE);
}
Expand All @@ -501,7 +537,7 @@ unsafe extern "C" fn x_truncate<T: Vfs>(
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
let size: usize = size.try_into().map_err(|_| vars::SQLITE_IOERR_TRUNCATE)?;
vfs.truncate(unsafe { file.handle.assume_init_mut() }, size)?;
vfs.truncate(&mut file.handle, size)?;
Ok(vars::SQLITE_OK)
})
}
Expand All @@ -510,7 +546,7 @@ unsafe extern "C" fn x_sync<T: Vfs>(p_file: *mut ffi::sqlite3_file, _flags: c_in
fallible(|| {
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
vfs.sync(unsafe { file.handle.assume_init_mut() })?;
vfs.sync(&mut file.handle)?;
Ok(vars::SQLITE_OK)
})
}
Expand All @@ -522,7 +558,7 @@ unsafe extern "C" fn x_file_size<T: Vfs>(
fallible(|| {
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
let size = vfs.file_size(unsafe { file.handle.assume_init_mut() })?;
let size = vfs.file_size(&mut file.handle)?;
let p_size = unsafe { p_size.as_mut() }.ok_or(vars::SQLITE_INTERNAL)?;
*p_size = size.try_into().map_err(|_| vars::SQLITE_IOERR_FSTAT)?;
Ok(vars::SQLITE_OK)
Expand All @@ -534,7 +570,7 @@ unsafe extern "C" fn x_lock<T: Vfs>(p_file: *mut ffi::sqlite3_file, raw_lock: c_
let level: LockLevel = raw_lock.into();
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
vfs.lock(unsafe { file.handle.assume_init_mut() }, level)?;
vfs.lock(&mut file.handle, level)?;
Ok(vars::SQLITE_OK)
})
}
Expand All @@ -544,7 +580,7 @@ unsafe extern "C" fn x_unlock<T: Vfs>(p_file: *mut ffi::sqlite3_file, raw_lock:
let level: LockLevel = raw_lock.into();
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
vfs.unlock(unsafe { file.handle.assume_init_mut() }, level)?;
vfs.unlock(&mut file.handle, level)?;
Ok(vars::SQLITE_OK)
})
}
Expand All @@ -557,7 +593,7 @@ unsafe extern "C" fn x_check_reserved_lock<T: Vfs>(
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
unsafe {
*p_out = vfs.check_reserved_lock(file.handle.assume_init_mut())? as c_int;
*p_out = vfs.check_reserved_lock(&mut file.handle)? as c_int;
}
Ok(vars::SQLITE_OK)
})
Expand Down Expand Up @@ -598,7 +634,7 @@ unsafe extern "C" fn x_file_control<T: Vfs>(
};
let pragma = Pragma { name: &name, arg: arg.as_deref() };

let (result, msg) = match vfs.pragma(unsafe { file.handle.assume_init_mut() }, pragma) {
let (result, msg) = match vfs.pragma(&mut file.handle, pragma) {
Ok(msg) => (Ok(vars::SQLITE_OK), msg),
Err(PragmaErr::NotFound) => (Err(vars::SQLITE_NOTFOUND), None),
Err(PragmaErr::Fail(err, msg)) => (Err(err), msg),
Expand All @@ -623,15 +659,87 @@ unsafe extern "C" fn x_sector_size<T: Vfs>(p_file: *mut ffi::sqlite3_file) -> c_
fallible(|| {
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
vfs.sector_size(unsafe { file.handle.assume_init_mut() })
vfs.sector_size(&mut file.handle)
})
}

unsafe extern "C" fn x_device_characteristics<T: Vfs>(p_file: *mut ffi::sqlite3_file) -> c_int {
fallible(|| {
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
vfs.device_characteristics(unsafe { file.handle.assume_init_mut() })
vfs.device_characteristics(&mut file.handle)
})
}

unsafe extern "C" fn x_shm_map<T: Vfs>(
p_file: *mut ffi::sqlite3_file,
pg: c_int,
pgsz: c_int,
extend: c_int,
p_page: *mut *mut c_void,
) -> c_int {
fallible(|| {
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
// Drop any prior mapped region
if let Some(region) = vfs.shm_map(
&mut file.handle,
pg.try_into().map_err(|_| vars::SQLITE_IOERR)?,
pgsz.try_into().map_err(|_| vars::SQLITE_IOERR)?,
extend != 0,
)? {
file.mapped.push(Box::new(region));
let region = file.mapped.last_mut().unwrap();
let slice = (**region).as_mut();
if slice.len() < pgsz as usize {
file.mapped.pop();
return Err(vars::SQLITE_IOERR);
}
unsafe { *p_page = slice.as_mut_ptr() as *mut c_void }
} else {
unsafe { *p_page = null_mut() }
}
Ok(vars::SQLITE_OK)
})
}

unsafe extern "C" fn x_shm_lock<T: Vfs>(
p_file: *mut ffi::sqlite3_file,
offset: c_int,
n: c_int,
flags: c_int,
) -> c_int {
fallible(|| {
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
vfs.shm_lock(
&mut file.handle,
offset.try_into().map_err(|_| vars::SQLITE_IOERR)?,
n.try_into().map_err(|_| vars::SQLITE_IOERR)?,
ShmLockMode::try_from(flags)?,
)?;
Ok(vars::SQLITE_OK)
})
}

unsafe extern "C" fn x_shm_barrier<T: Vfs>(p_file: *mut ffi::sqlite3_file) {
if let Ok(file) = unwrap_file!(p_file, T) {
if let Ok(vfs) = unwrap_vfs!(file.vfs, T) {
vfs.shm_barrier(&mut file.handle)
}
}
}

unsafe extern "C" fn x_shm_unmap<T: Vfs>(
p_file: *mut ffi::sqlite3_file,
delete_flag: c_int,
) -> c_int {
fallible(|| {
let file = unwrap_file!(p_file, T)?;
let vfs = unwrap_vfs!(file.vfs, T)?;
drop(file.mapped.drain(..));
vfs.shm_unmap(&mut file.handle, delete_flag != 0)?;
Ok(vars::SQLITE_OK)
})
}

Expand Down
Loading