diff --git a/examples/memvfs.rs b/examples/memvfs.rs index 7652c39..2c58dbe 100644 --- a/examples/memvfs.rs +++ b/examples/memvfs.rs @@ -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 { log::debug!("open: path={:?}, opts={:?}", path, opts); diff --git a/src/flags.rs b/src/flags.rs index b5254ae..3699b74 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -178,3 +178,29 @@ impl From for LockLevel { } } } + +#[derive(Copy, Clone, Debug)] +pub enum ShmLockMode { + LockShared, + LockExclusive, + UnlockShared, + UnlockExclusive, +} + +impl TryFrom for ShmLockMode { + type Error = i32; + + fn try_from(flags: i32) -> Result { + 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), + }) + } +} diff --git a/src/mock.rs b/src/mock.rs index 3be78db..f9f638a 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -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> { let mut state = self.state(); diff --git a/src/vfs.rs b/src/vfs.rs index 186e432..23e49ee 100644 --- a/src/vfs.rs +++ b/src/vfs.rs @@ -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}; @@ -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}, @@ -42,10 +43,12 @@ pub type VfsResult = Result; // 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 { +struct FileWrapper { file: ffi::sqlite3_file, vfs: *mut ffi::sqlite3_vfs, - handle: MaybeUninit, + handle: T::Handle, + // Mapped region handles + mapped: Vec>, } struct AppData { @@ -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::::Handle>>() + let out: VfsResult<&mut FileWrapper<$t_vfs>> = $p_file + .cast::>() .as_mut() .ok_or(vars::SQLITE_INTERNAL); out @@ -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> { @@ -186,6 +190,32 @@ pub trait Vfs: Send + Sync { fn device_characteristics(&self, handle: &mut Self::Handle) -> VfsResult { Ok(DEFAULT_DEVICE_CHARACTERISTICS) } + + fn shm_map( + &self, + handle: &mut Self::Handle, + region_idx: usize, + region_size: usize, + extend: bool, + ) -> VfsResult> { + 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)] @@ -300,10 +330,10 @@ fn register_inner( xFileControl: Some(x_file_control::), xSectorSize: Some(x_sector_size::), xDeviceCharacteristics: Some(x_device_characteristics::), - xShmMap: None, - xShmLock: None, - xShmBarrier: None, - xShmUnmap: None, + xShmMap: Some(x_shm_map::), + xShmLock: Some(x_shm_lock::), + xShmBarrier: Some(x_shm_barrier::), + xShmUnmap: Some(x_shm_unmap::), xFetch: None, xUnfetch: None, }; @@ -315,7 +345,7 @@ fn register_inner( 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::>() + let filewrapper_size: c_int = size_of::>() .try_into() .map_err(|_| vars::SQLITE_INTERNAL)?; @@ -371,7 +401,6 @@ unsafe extern "C" fn x_open( 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() } { @@ -385,9 +414,18 @@ unsafe extern "C" fn x_open( *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::>(); + 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) }) @@ -447,11 +485,9 @@ unsafe extern "C" fn x_full_pathname( unsafe extern "C" fn x_close(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::>()) }; 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) }) } @@ -468,7 +504,7 @@ unsafe extern "C" fn x_read( 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::(), buf_len) }; - vfs.read(unsafe { file.handle.assume_init_mut() }, offset, buf)?; + vfs.read(&mut file.handle, offset, buf)?; Ok(vars::SQLITE_OK) }) } @@ -485,7 +521,7 @@ unsafe extern "C" fn x_write( 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::(), 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); } @@ -501,7 +537,7 @@ unsafe extern "C" fn x_truncate( 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) }) } @@ -510,7 +546,7 @@ unsafe extern "C" fn x_sync(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) }) } @@ -522,7 +558,7 @@ unsafe extern "C" fn x_file_size( 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) @@ -534,7 +570,7 @@ unsafe extern "C" fn x_lock(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) }) } @@ -544,7 +580,7 @@ unsafe extern "C" fn x_unlock(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) }) } @@ -557,7 +593,7 @@ unsafe extern "C" fn x_check_reserved_lock( 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) }) @@ -598,7 +634,7 @@ unsafe extern "C" fn x_file_control( }; 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), @@ -623,7 +659,7 @@ unsafe extern "C" fn x_sector_size(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) }) } @@ -631,7 +667,79 @@ unsafe extern "C" fn x_device_characteristics(p_file: *mut ffi::sqlite3_ 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( + 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( + 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(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( + 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) }) }