Skip to content
Merged
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
70 changes: 49 additions & 21 deletions src/vfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use alloc::boxed::Box;
use alloc::ffi::CString;
use alloc::format;
use alloc::string::String;
use core::mem::{self, ManuallyDrop, MaybeUninit, size_of};
use core::mem::{ManuallyDrop, size_of};
use core::slice;
use core::{
ffi::{CStr, c_char, c_int, c_void},
Expand Down Expand Up @@ -45,7 +45,7 @@ pub type VfsResult<T> = Result<T, SqliteErr>;
struct FileWrapper<Handle> {
file: ffi::sqlite3_file,
vfs: *mut ffi::sqlite3_vfs,
handle: MaybeUninit<Handle>,
handle: Handle,
}

struct AppData<Vfs> {
Expand Down Expand Up @@ -371,7 +371,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 +384,19 @@ 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::Handle>>();
// Safety: SQLite owns the heap allocation backing p_file (it handles malloc/free).
// We use ptr::write to initialize that allocation directly.
unsafe {
core::ptr::write(
out_file,
FileWrapper {
file: ffi::sqlite3_file { pMethods: &appdata.io_methods },
vfs: p_vfs,
handle,
},
);
}

Ok(vars::SQLITE_OK)
})
Expand Down Expand Up @@ -447,10 +456,23 @@ 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 vfs = unwrap_vfs!(file.vfs, T)?;
let handle = mem::replace(&mut file.handle, MaybeUninit::uninit());
let handle = unsafe { handle.assume_init() };
// Safety: SQLite owns the heap allocation backing p_file (it handles
// malloc/free). We use ptr::read to copy our Handle out of that
// allocation so it can be passed to vfs.close() and properly dropped.
// SQLite will not call any other file methods after x_close without
// first calling x_open to reinitialize the handle.
let (vfs, handle) = unsafe {
// verify p_file is not null and get a mutable reference
let p_file_ref = p_file.as_mut().ok_or(vars::SQLITE_INTERNAL)?;
// set pMethods to null, signaling to SQLite that the file is closed
p_file_ref.pMethods = core::ptr::null();

// extract a copy of the FileWrapper
let file = core::ptr::read(p_file.cast::<FileWrapper<T::Handle>>());
(file.vfs, file.handle)
};

let vfs = unwrap_vfs!(vfs, T)?;
vfs.close(handle)?;
Ok(vars::SQLITE_OK)
})
Expand All @@ -468,7 +490,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 +507,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 +523,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 +532,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 +544,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 +556,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 +566,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 +579,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 +620,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 +645,15 @@ 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)
})
}

Expand Down Expand Up @@ -818,13 +840,19 @@ mod tests {
let n = blob.write(b"hello")?;
assert_eq!(n, 5);

blob.close()?;

// query the table for the blob and print it
let mut stmt = conn.prepare("select data from b")?;
let mut rows = stmt.query([])?;
while let Some(row) = rows.next()? {
let data: Vec<u8> = row.get(0)?;
assert_eq!(&data[0..5], b"hello");
}
drop(rows);
drop(stmt);

conn.close().expect("failed to close connection");

Ok(())
}
Expand Down