1#![allow(clippy::question_mark)]
2use std::borrow::Cow;
6use std::collections::HashMap;
7use std::ffi::{c_void, CStr, CString};
8use std::io::ErrorKind;
9use std::mem::{size_of, ManuallyDrop, MaybeUninit};
10use std::ops::Range;
11use std::os::raw::{c_char, c_int};
12use std::pin::Pin;
13use std::ptr::null_mut;
14use std::slice;
15use std::sync::{Arc, Mutex};
16use std::time::Duration;
17
18mod ffi;
19
20pub trait DatabaseHandle: Sync {
22 type WalIndex: wip::WalIndex;
24
25 fn size(&self) -> Result<u64, std::io::Error>;
27
28 fn read_exact_at(&mut self, buf: &mut [u8], offset: u64) -> Result<(), std::io::Error>;
30
31 fn write_all_at(&mut self, buf: &[u8], offset: u64) -> Result<(), std::io::Error>;
33
34 fn sync(&mut self, data_only: bool) -> Result<(), std::io::Error>;
37
38 fn set_len(&mut self, size: u64) -> Result<(), std::io::Error>;
40
41 fn lock(&mut self, lock: LockKind) -> Result<bool, std::io::Error>;
47
48 fn unlock(&mut self, lock: LockKind) -> Result<bool, std::io::Error> {
50 self.lock(lock)
51 }
52
53 fn reserved(&mut self) -> Result<bool, std::io::Error>;
56
57 fn current_lock(&self) -> Result<LockKind, std::io::Error>;
59
60 fn set_chunk_size(&self, _chunk_size: usize) -> Result<(), std::io::Error> {
62 Ok(())
63 }
64
65 fn moved(&self) -> Result<bool, std::io::Error> {
68 Ok(false)
69 }
70
71 fn wal_index(&self, readonly: bool) -> Result<Self::WalIndex, std::io::Error>;
72}
73
74pub trait Vfs: Sync {
76 type Handle: DatabaseHandle;
78
79 fn open(&self, db: &str, opts: OpenOptions) -> Result<Self::Handle, std::io::Error>;
81
82 fn delete(&self, db: &str) -> Result<(), std::io::Error>;
84
85 fn exists(&self, db: &str) -> Result<bool, std::io::Error>;
87
88 fn temporary_name(&self) -> String;
90
91 fn random(&self, buffer: &mut [i8]);
93
94 fn sleep(&self, duration: Duration) -> Duration;
96
97 fn access(&self, _db: &str, _write: bool) -> Result<bool, std::io::Error> {
99 Ok(true)
100 }
101
102 fn full_pathname<'a>(&self, db: &'a str) -> Result<Cow<'a, str>, std::io::Error> {
104 Ok(db.into())
105 }
106}
107
108#[doc(hidden)]
109pub mod wip {
110 use super::*;
111
112 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
113 #[repr(u16)]
114 pub enum WalIndexLock {
115 None = 1,
116 Shared,
117 Exclusive,
118 }
119
120 pub trait WalIndex: Sync {
121 fn enabled() -> bool {
122 true
123 }
124
125 fn map(&mut self, region: u32) -> Result<[u8; 32768], std::io::Error>;
126 fn lock(&mut self, locks: Range<u8>, lock: WalIndexLock) -> Result<bool, std::io::Error>;
127 fn delete(self) -> Result<(), std::io::Error>;
128
129 fn pull(&mut self, _region: u32, _data: &mut [u8; 32768]) -> Result<(), std::io::Error> {
130 Ok(())
131 }
132
133 fn push(&mut self, _region: u32, _data: &[u8; 32768]) -> Result<(), std::io::Error> {
134 Ok(())
135 }
136 }
137}
138
139#[derive(Debug, Clone, PartialEq)]
140pub struct OpenOptions {
141 pub kind: OpenKind,
143
144 pub access: OpenAccess,
146
147 delete_on_close: bool,
149}
150
151#[derive(Debug, Clone, Copy, PartialEq)]
153pub enum OpenKind {
154 MainDb,
155 MainJournal,
156 TempDb,
157 TempJournal,
158 TransientDb,
159 SubJournal,
160 SuperJournal,
161 Wal,
162}
163
164#[derive(Debug, Clone, Copy, PartialEq)]
166pub enum OpenAccess {
167 Read,
169
170 Write,
172
173 Create,
175
176 CreateNew,
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub enum LockKind {
183 None,
188
189 Shared,
194
195 Reserved,
202
203 Pending,
209
210 Exclusive,
215}
216
217struct State<V> {
218 name: CString,
219 vfs: Arc<V>,
220 #[cfg(any(feature = "syscall", feature = "loadext"))]
221 parent_vfs: *mut ffi::sqlite3_vfs,
222 io_methods: ffi::sqlite3_io_methods,
223 last_error: Arc<Mutex<Option<(i32, std::io::Error)>>>,
224 next_id: usize,
225}
226
227pub fn register<F: DatabaseHandle, V: Vfs<Handle = F>>(
229 name: &str,
230 vfs: V,
231 as_default: bool,
232) -> Result<(), RegisterError> {
233 let io_methods = ffi::sqlite3_io_methods {
234 iVersion: 2,
235 xClose: Some(io::close::<V, F>),
236 xRead: Some(io::read::<V, F>),
237 xWrite: Some(io::write::<V, F>),
238 xTruncate: Some(io::truncate::<V, F>),
239 xSync: Some(io::sync::<V, F>),
240 xFileSize: Some(io::file_size::<V, F>),
241 xLock: Some(io::lock::<V, F>),
242 xUnlock: Some(io::unlock::<V, F>),
243 xCheckReservedLock: Some(io::check_reserved_lock::<V, F>),
244 xFileControl: Some(io::file_control::<V, F>),
245 xSectorSize: Some(io::sector_size::<F>),
246 xDeviceCharacteristics: Some(io::device_characteristics::<V, F>),
247 xShmMap: Some(io::shm_map::<V, F>),
248 xShmLock: Some(io::shm_lock::<V, F>),
249 xShmBarrier: Some(io::shm_barrier::<V, F>),
250 xShmUnmap: Some(io::shm_unmap::<V, F>),
251 xFetch: None,
252 xUnfetch: None,
253 };
254 let name = CString::new(name)?;
255 let name_ptr = name.as_ptr();
256 let ptr = Box::into_raw(Box::new(State {
257 name,
258 vfs: Arc::new(vfs),
259 #[cfg(any(feature = "syscall", feature = "loadext"))]
260 parent_vfs: unsafe { ffi::sqlite3_vfs_find(std::ptr::null_mut()) },
261 io_methods,
262 last_error: Default::default(),
263 next_id: 0,
264 }));
265 let vfs = Box::into_raw(Box::new(ffi::sqlite3_vfs {
266 #[cfg(not(feature = "syscall"))]
267 iVersion: 2,
268 #[cfg(feature = "syscall")]
269 iVersion: 3,
270 szOsFile: size_of::<FileState<V, F>>() as i32,
271 mxPathname: MAX_PATH_LENGTH as i32, pNext: null_mut(),
273 zName: name_ptr,
274 pAppData: ptr as _,
275 xOpen: Some(vfs::open::<F, V>),
276 xDelete: Some(vfs::delete::<V>),
277 xAccess: Some(vfs::access::<V>),
278 xFullPathname: Some(vfs::full_pathname::<V>),
279 xDlOpen: Some(vfs::dlopen::<V>),
280 xDlError: Some(vfs::dlerror::<V>),
281 xDlSym: Some(vfs::dlsym::<V>),
282 xDlClose: Some(vfs::dlclose::<V>),
283 xRandomness: Some(vfs::randomness::<V>),
284 xSleep: Some(vfs::sleep::<V>),
285 xCurrentTime: Some(vfs::current_time::<V>),
286 xGetLastError: Some(vfs::get_last_error::<V>),
287 xCurrentTimeInt64: Some(vfs::current_time_int64::<V>),
288
289 #[cfg(not(feature = "syscall"))]
290 xSetSystemCall: None,
291 #[cfg(not(feature = "syscall"))]
292 xGetSystemCall: None,
293 #[cfg(not(feature = "syscall"))]
294 xNextSystemCall: None,
295
296 #[cfg(feature = "syscall")]
297 xSetSystemCall: Some(vfs::set_system_call::<V>),
298 #[cfg(feature = "syscall")]
299 xGetSystemCall: Some(vfs::get_system_call::<V>),
300 #[cfg(feature = "syscall")]
301 xNextSystemCall: Some(vfs::next_system_call::<V>),
302 }));
303
304 let result = unsafe { ffi::sqlite3_vfs_register(vfs, as_default as i32) };
305 if result != ffi::SQLITE_OK {
306 return Err(RegisterError::Register(result));
307 }
308
309 Ok(())
312}
313
314const MAX_PATH_LENGTH: usize = 512;
316
317#[repr(C)]
318struct FileState<V, F: DatabaseHandle> {
319 base: ffi::sqlite3_file,
320 ext: MaybeUninit<FileExt<V, F>>,
321}
322
323#[repr(C)]
324struct FileExt<V, F: DatabaseHandle> {
325 vfs: Arc<V>,
326 vfs_name: CString,
327 db_name: String,
328 file: F,
329 delete_on_close: bool,
330 last_error: Arc<Mutex<Option<(i32, std::io::Error)>>>,
332 last_errno: i32,
334 wal_index: Option<(F::WalIndex, bool)>,
335 wal_index_regions: HashMap<u32, Pin<Box<[u8; 32768]>>>,
336 wal_index_locks: HashMap<u8, wip::WalIndexLock>,
337 has_exclusive_lock: bool,
338 id: usize,
339 chunk_size: Option<usize>,
340 persist_wal: bool,
341 powersafe_overwrite: bool,
342}
343
344mod vfs {
347 use super::*;
348
349 pub unsafe extern "C" fn open<F: DatabaseHandle, V: Vfs<Handle = F>>(
351 p_vfs: *mut ffi::sqlite3_vfs,
352 z_name: *const c_char,
353 p_file: *mut ffi::sqlite3_file,
354 flags: c_int,
355 p_out_flags: *mut c_int,
356 ) -> c_int {
357 let state = match vfs_state::<V>(p_vfs) {
358 Ok(state) => state,
359 Err(_) => return ffi::SQLITE_ERROR,
360 };
361
362 let name = if z_name.is_null() {
363 None
364 } else {
365 match CStr::from_ptr(z_name).to_str() {
366 Ok(name) => Some(name),
367 Err(_) => {
368 return state.set_last_error(
369 ffi::SQLITE_CANTOPEN,
370 std::io::Error::new(
371 ErrorKind::Other,
372 format!(
373 "open failed: database must be valid utf8 (received: {:?})",
374 CStr::from_ptr(z_name)
375 ),
376 ),
377 )
378 }
379 }
380 };
381 log::trace!("open z_name={:?} flags={}", name, flags);
382
383 let mut opts = match OpenOptions::from_flags(flags) {
384 Some(opts) => opts,
385 None => {
386 return state.set_last_error(
387 ffi::SQLITE_CANTOPEN,
388 std::io::Error::new(ErrorKind::Other, "invalid open flags"),
389 );
390 }
391 };
392
393 if z_name.is_null() && !opts.delete_on_close {
394 return state.set_last_error(
395 ffi::SQLITE_CANTOPEN,
396 std::io::Error::new(
397 ErrorKind::Other,
398 "delete on close expected for temporary database",
399 ),
400 );
401 }
402
403 let out_file = match (p_file as *mut FileState<V, F>).as_mut() {
404 Some(f) => f,
405 None => {
406 return state.set_last_error(
407 ffi::SQLITE_CANTOPEN,
408 std::io::Error::new(ErrorKind::Other, "invalid file pointer"),
409 );
410 }
411 };
412
413 let mut powersafe_overwrite = true;
414 if flags & ffi::SQLITE_OPEN_URI > 0 && name.is_some() {
415 let param = b"psow\0";
416 if ffi::sqlite3_uri_boolean(z_name, param.as_ptr() as *const c_char, 1) == 0 {
417 powersafe_overwrite = false;
418 }
419 }
420
421 let name = name.map_or_else(|| state.vfs.temporary_name(), String::from);
422 let result = state.vfs.open(&name, opts.clone());
423 let result = match result {
424 Ok(f) => Ok(f),
425 Err(err)
427 if err.kind() == ErrorKind::PermissionDenied
428 && matches!(
429 opts.kind,
430 OpenKind::SuperJournal | OpenKind::MainJournal | OpenKind::Wal
431 )
432 && matches!(opts.access, OpenAccess::Create | OpenAccess::CreateNew)
433 && !state.vfs.exists(&name).unwrap_or(false) =>
434 {
435 return state.set_last_error(ffi::SQLITE_READONLY_DIRECTORY, err);
436 }
437
438 Err(err)
440 if err.kind() == ErrorKind::PermissionDenied && opts.access != OpenAccess::Read =>
441 {
442 opts.access = OpenAccess::Read;
443 state.vfs.open(&name, opts.clone()).map_err(|_| err)
444 }
445
446 Err(err) if err.kind() == ErrorKind::Other && opts.access == OpenAccess::Read => {
448 return state.set_last_error(ffi::SQLITE_IOERR, err);
449 }
450
451 Err(err) => Err(err),
452 };
453 let file = match result {
454 Ok(f) => f,
455 Err(err) => {
456 return state.set_last_error(ffi::SQLITE_CANTOPEN, err);
457 }
458 };
459
460 if let Some(p_out_flags) = p_out_flags.as_mut() {
461 *p_out_flags = opts.to_flags();
462 }
463
464 out_file.base.pMethods = &state.io_methods;
465 out_file.ext.write(FileExt {
466 vfs: state.vfs.clone(),
467 vfs_name: state.name.clone(),
468 db_name: name,
469 file,
470 delete_on_close: opts.delete_on_close,
471 last_error: Arc::clone(&state.last_error),
472 last_errno: 0,
473 wal_index: None,
474 wal_index_regions: Default::default(),
475 wal_index_locks: Default::default(),
476 has_exclusive_lock: false,
477 id: state.next_id,
478 chunk_size: None,
479 persist_wal: false,
480 powersafe_overwrite,
481 });
482 state.next_id = state.next_id.overflowing_add(1).0;
483
484 #[cfg(feature = "sqlite_test")]
485 ffi::sqlite3_inc_open_file_count();
486
487 ffi::SQLITE_OK
488 }
489
490 pub unsafe extern "C" fn delete<V: Vfs>(
493 p_vfs: *mut ffi::sqlite3_vfs,
494 z_path: *const c_char,
495 _sync_dir: c_int,
496 ) -> c_int {
497 let state = match vfs_state::<V>(p_vfs) {
503 Ok(state) => state,
504 Err(_) => return ffi::SQLITE_DELETE,
505 };
506
507 let path = match CStr::from_ptr(z_path).to_str() {
508 Ok(name) => name,
509 Err(_) => {
510 return state.set_last_error(
511 ffi::SQLITE_ERROR,
512 std::io::Error::new(
513 ErrorKind::Other,
514 format!(
515 "delete failed: database must be valid utf8 (received: {:?})",
516 CStr::from_ptr(z_path)
517 ),
518 ),
519 )
520 }
521 };
522 log::trace!("delete name={}", path);
523
524 match state.vfs.delete(path) {
525 Ok(_) => ffi::SQLITE_OK,
526 Err(err) => {
527 if err.kind() == ErrorKind::NotFound {
528 ffi::SQLITE_IOERR_DELETE_NOENT
529 } else {
530 state.set_last_error(ffi::SQLITE_DELETE, err)
531 }
532 }
533 }
534 }
535
536 pub unsafe extern "C" fn access<V: Vfs>(
539 p_vfs: *mut ffi::sqlite3_vfs,
540 z_path: *const c_char,
541 flags: c_int,
542 p_res_out: *mut c_int,
543 ) -> c_int {
544 #[cfg(feature = "sqlite_test")]
545 if simulate_io_error() {
546 return ffi::SQLITE_IOERR_ACCESS;
547 }
548
549 let state = match vfs_state::<V>(p_vfs) {
550 Ok(state) => state,
551 Err(_) => return ffi::SQLITE_ERROR,
552 };
553
554 let path = match CStr::from_ptr(z_path).to_str() {
555 Ok(name) => name,
556 Err(_) => {
557 log::warn!(
558 "access failed: database must be valid utf8 (received: {:?})",
559 CStr::from_ptr(z_path)
560 );
561
562 if let Ok(p_res_out) = p_res_out.as_mut().ok_or_else(null_ptr_error) {
563 *p_res_out = false as i32;
564 }
565
566 return ffi::SQLITE_OK;
567 }
568 };
569 log::trace!("access z_name={} flags={}", path, flags);
570
571 let result = match flags {
572 ffi::SQLITE_ACCESS_EXISTS => state.vfs.exists(path),
573 ffi::SQLITE_ACCESS_READ => state.vfs.access(path, false),
574 ffi::SQLITE_ACCESS_READWRITE => state.vfs.access(path, true),
575 _ => return ffi::SQLITE_IOERR_ACCESS,
576 };
577
578 if let Err(err) = result.and_then(|ok| {
579 let p_res_out: &mut c_int = p_res_out.as_mut().ok_or_else(null_ptr_error)?;
580 *p_res_out = ok as i32;
581 Ok(())
582 }) {
583 return state.set_last_error(ffi::SQLITE_IOERR_ACCESS, err);
584 }
585
586 ffi::SQLITE_OK
587 }
588
589 pub unsafe extern "C" fn full_pathname<V: Vfs>(
593 p_vfs: *mut ffi::sqlite3_vfs,
594 z_path: *const c_char,
595 n_out: c_int,
596 z_out: *mut c_char,
597 ) -> c_int {
598 let state = match vfs_state::<V>(p_vfs) {
604 Ok(state) => state,
605 Err(_) => return ffi::SQLITE_ERROR,
606 };
607
608 let path = match CStr::from_ptr(z_path).to_str() {
609 Ok(name) => name,
610 Err(_) => {
611 return state.set_last_error(
612 ffi::SQLITE_ERROR,
613 std::io::Error::new(
614 ErrorKind::Other,
615 format!(
616 "full_pathname failed: database must be valid utf8 (received: {:?})",
617 CStr::from_ptr(z_path)
618 ),
619 ),
620 )
621 }
622 };
623 log::trace!("full_pathname name={}", path);
624
625 let name = match state.vfs.full_pathname(path).and_then(|name| {
626 CString::new(name.to_string()).map_err(|_| {
627 std::io::Error::new(ErrorKind::Other, "name must not contain a nul byte")
628 })
629 }) {
630 Ok(name) => name,
631 Err(err) => return state.set_last_error(ffi::SQLITE_ERROR, err),
632 };
633
634 let name = name.to_bytes_with_nul();
635 if name.len() > n_out as usize || name.len() > MAX_PATH_LENGTH {
636 return state.set_last_error(
637 ffi::SQLITE_CANTOPEN,
638 std::io::Error::new(ErrorKind::Other, "full pathname is too long"),
639 );
640 }
641 let out = slice::from_raw_parts_mut(z_out as *mut u8, name.len());
642 out.copy_from_slice(name);
643
644 ffi::SQLITE_OK
645 }
646
647 #[allow(unused_variables)]
649 pub unsafe extern "C" fn dlopen<V>(
650 p_vfs: *mut ffi::sqlite3_vfs,
651 z_path: *const c_char,
652 ) -> *mut c_void {
653 log::trace!("dlopen");
654
655 #[cfg(feature = "loadext")]
656 {
657 let state = match vfs_state::<V>(p_vfs) {
658 Ok(state) => state,
659 Err(_) => return null_mut(),
660 };
661
662 if let Some(dlopen) = state.parent_vfs.as_ref().and_then(|v| v.xDlOpen) {
663 return dlopen(state.parent_vfs, z_path);
664 }
665 }
666
667 null_mut()
668 }
669
670 #[allow(unused_variables)]
673 pub unsafe extern "C" fn dlerror<V>(
674 p_vfs: *mut ffi::sqlite3_vfs,
675 n_byte: c_int,
676 z_err_msg: *mut c_char,
677 ) {
678 log::trace!("dlerror");
679
680 #[cfg(feature = "loadext")]
681 {
682 let state = match vfs_state::<V>(p_vfs) {
683 Ok(state) => state,
684 Err(_) => return,
685 };
686
687 if let Some(dlerror) = state.parent_vfs.as_ref().and_then(|v| v.xDlError) {
688 return dlerror(state.parent_vfs, n_byte, z_err_msg);
689 }
690
691 return;
692 }
693
694 #[cfg(not(feature = "loadext"))]
695 {
696 let msg = concat!("Loadable extensions are not supported", "\0");
697 ffi::sqlite3_snprintf(n_byte, z_err_msg, msg.as_ptr() as _);
698 }
699 }
700
701 #[allow(unused_variables)]
703 pub unsafe extern "C" fn dlsym<V>(
704 p_vfs: *mut ffi::sqlite3_vfs,
705 p: *mut c_void,
706 z_sym: *const c_char,
707 ) -> Option<unsafe extern "C" fn(*mut ffi::sqlite3_vfs, *mut c_void, *const c_char)> {
708 log::trace!("dlsym");
709
710 #[cfg(feature = "loadext")]
711 {
712 let state = match vfs_state::<V>(p_vfs) {
713 Ok(state) => state,
714 Err(_) => return None,
715 };
716
717 if let Some(dlsym) = state.parent_vfs.as_ref().and_then(|v| v.xDlSym) {
718 return dlsym(state.parent_vfs, p, z_sym);
719 }
720 }
721
722 None
723 }
724
725 #[allow(unused_variables)]
727 pub unsafe extern "C" fn dlclose<V>(p_vfs: *mut ffi::sqlite3_vfs, p_handle: *mut c_void) {
728 log::trace!("dlclose");
729
730 #[cfg(feature = "loadext")]
731 {
732 let state = match vfs_state::<V>(p_vfs) {
733 Ok(state) => state,
734 Err(_) => return,
735 };
736
737 if let Some(dlclose) = state.parent_vfs.as_ref().and_then(|v| v.xDlClose) {
738 return dlclose(state.parent_vfs, p_handle);
739 }
740 }
741 }
742
743 pub unsafe extern "C" fn randomness<V: Vfs>(
745 p_vfs: *mut ffi::sqlite3_vfs,
746 n_byte: c_int,
747 z_buf_out: *mut c_char,
748 ) -> c_int {
749 log::trace!("randomness");
750
751 let bytes = slice::from_raw_parts_mut(z_buf_out as *mut i8, n_byte as usize);
752 if cfg!(feature = "sqlite_test") {
753 bytes.fill(0);
755 } else {
756 let state = match vfs_state::<V>(p_vfs) {
757 Ok(state) => state,
758 Err(_) => return 0,
759 };
760
761 state.vfs.random(bytes);
762 }
763 bytes.len() as c_int
764 }
765
766 pub unsafe extern "C" fn sleep<V: Vfs>(p_vfs: *mut ffi::sqlite3_vfs, n_micro: c_int) -> c_int {
768 log::trace!("sleep");
769
770 let state = match vfs_state::<V>(p_vfs) {
771 Ok(state) => state,
772 Err(_) => return ffi::SQLITE_ERROR,
773 };
774 state
775 .vfs
776 .sleep(Duration::from_micros(n_micro as u64))
777 .as_micros() as c_int
778 }
779
780 pub unsafe extern "C" fn current_time<V>(
782 p_vfs: *mut ffi::sqlite3_vfs,
783 p_time_out: *mut f64,
784 ) -> c_int {
785 log::trace!("current_time");
786
787 let mut i = 0i64;
788 current_time_int64::<V>(p_vfs, &mut i);
789
790 *p_time_out = i as f64 / 86400000.0;
791 ffi::SQLITE_OK
792 }
793
794 pub unsafe extern "C" fn current_time_int64<V>(
795 _p_vfs: *mut ffi::sqlite3_vfs,
796 p: *mut i64,
797 ) -> i32 {
798 log::trace!("current_time_int64");
799
800 const UNIX_EPOCH: i64 = 24405875 * 8640000;
801 let now = if cfg!(all(target_arch = "wasm32", target_os = "unknown")) {
802 (ic_cdk::api::time() as f64 / 1000000000.0) as i64 + UNIX_EPOCH
803 } else {
804 std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as i64 + UNIX_EPOCH
805 };
806 #[cfg(feature = "sqlite_test")]
807 let now = if ffi::sqlite3_get_current_time() > 0 {
808 ffi::sqlite3_get_current_time() as i64 * 1000 + UNIX_EPOCH
809 } else {
810 now
811 };
812
813 *p = now;
814 ffi::SQLITE_OK
815 }
816
817 #[cfg(feature = "syscall")]
818 pub unsafe extern "C" fn set_system_call<V>(
819 p_vfs: *mut ffi::sqlite3_vfs,
820 z_name: *const ::std::os::raw::c_char,
821 p_new_func: ffi::sqlite3_syscall_ptr,
822 ) -> ::std::os::raw::c_int {
823 let state = match vfs_state::<V>(p_vfs) {
824 Ok(state) => state,
825 Err(_) => return ffi::SQLITE_ERROR,
826 };
827
828 if let Some(set_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xSetSystemCall) {
829 return set_system_call(state.parent_vfs, z_name, p_new_func);
830 }
831
832 ffi::SQLITE_ERROR
833 }
834
835 #[cfg(feature = "syscall")]
836 pub unsafe extern "C" fn get_system_call<V>(
837 p_vfs: *mut ffi::sqlite3_vfs,
838 z_name: *const ::std::os::raw::c_char,
839 ) -> ffi::sqlite3_syscall_ptr {
840 let state = match vfs_state::<V>(p_vfs) {
841 Ok(state) => state,
842 Err(_) => return None,
843 };
844
845 if let Some(get_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xGetSystemCall) {
846 return get_system_call(state.parent_vfs, z_name);
847 }
848
849 None
850 }
851
852 #[cfg(feature = "syscall")]
853 pub unsafe extern "C" fn next_system_call<V>(
854 p_vfs: *mut ffi::sqlite3_vfs,
855 z_name: *const ::std::os::raw::c_char,
856 ) -> *const ::std::os::raw::c_char {
857 let state = match vfs_state::<V>(p_vfs) {
858 Ok(state) => state,
859 Err(_) => return std::ptr::null(),
860 };
861
862 if let Some(next_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xNextSystemCall) {
863 return next_system_call(state.parent_vfs, z_name);
864 }
865
866 std::ptr::null()
867 }
868
869 pub unsafe extern "C" fn get_last_error<V>(
870 p_vfs: *mut ffi::sqlite3_vfs,
871 n_byte: c_int,
872 z_err_msg: *mut c_char,
873 ) -> c_int {
874 let state = match vfs_state::<V>(p_vfs) {
875 Ok(state) => state,
876 Err(_) => return ffi::SQLITE_ERROR,
877 };
878 if let Some((eno, err)) = state.last_error.lock().unwrap().as_ref() {
879 let msg = match CString::new(err.to_string()) {
880 Ok(msg) => msg,
881 Err(_) => return ffi::SQLITE_ERROR,
882 };
883
884 let msg = msg.to_bytes_with_nul();
885 if msg.len() > n_byte as usize {
886 return ffi::SQLITE_ERROR;
887 }
888 let out = slice::from_raw_parts_mut(z_err_msg as *mut u8, msg.len());
889 out.copy_from_slice(msg);
890
891 return *eno;
892 }
893 ffi::SQLITE_OK
894 }
895}
896
897mod io {
898 use std::collections::hash_map::Entry;
899 use std::mem;
900
901 use super::*;
902 use wip::WalIndex;
903
904 pub unsafe extern "C" fn close<V: Vfs, F: DatabaseHandle>(
906 p_file: *mut ffi::sqlite3_file,
907 ) -> c_int {
908 if let Some(f) = (p_file as *mut FileState<V, F>).as_mut() {
909 let ext = f.ext.assume_init_mut();
910 if ext.delete_on_close {
911 if let Err(err) = ext.vfs.delete(&ext.db_name) {
912 return ext.set_last_error(ffi::SQLITE_DELETE, err);
913 }
914 }
915
916 let ext = mem::replace(&mut f.ext, MaybeUninit::uninit());
917 let ext = ext.assume_init(); log::trace!("[{}] close ({})", ext.id, ext.db_name);
919 }
920
921 #[cfg(feature = "sqlite_test")]
922 ffi::sqlite3_dec_open_file_count();
923
924 ffi::SQLITE_OK
925 }
926
927 pub unsafe extern "C" fn read<V, F: DatabaseHandle>(
929 p_file: *mut ffi::sqlite3_file,
930 z_buf: *mut c_void,
931 i_amt: c_int,
932 i_ofst: ffi::sqlite3_int64,
933 ) -> c_int {
934 let state = match file_state::<V, F>(p_file) {
935 Ok(f) => f,
936 Err(_) => return ffi::SQLITE_IOERR_CLOSE,
937 };
938 log::trace!(
939 "[{}] read offset={} len={} ({})",
940 state.id,
941 i_ofst,
942 i_amt,
943 state.db_name
944 );
945
946 let out = slice::from_raw_parts_mut(z_buf as *mut u8, i_amt as usize);
947 if let Err(err) = state.file.read_exact_at(out, i_ofst as u64) {
948 let kind = err.kind();
949 if kind == ErrorKind::UnexpectedEof {
950 return ffi::SQLITE_IOERR_SHORT_READ;
951 } else {
952 return state.set_last_error(ffi::SQLITE_IOERR_READ, err);
953 }
954 }
955
956 ffi::SQLITE_OK
957 }
958
959 pub unsafe extern "C" fn write<V, F: DatabaseHandle>(
961 p_file: *mut ffi::sqlite3_file,
962 z: *const c_void,
963 i_amt: c_int,
964 i_ofst: ffi::sqlite3_int64,
965 ) -> c_int {
966 let state = match file_state::<V, F>(p_file) {
967 Ok(f) => f,
968 Err(_) => return ffi::SQLITE_IOERR_WRITE,
969 };
970 log::trace!(
971 "[{}] write offset={} len={} ({})",
972 state.id,
973 i_ofst,
974 i_amt,
975 state.db_name
976 );
977
978 let data = slice::from_raw_parts(z as *mut u8, i_amt as usize);
979 let result = state.file.write_all_at(data, i_ofst as u64);
980
981 #[cfg(feature = "sqlite_test")]
982 let result = if simulate_io_error() {
983 Err(ErrorKind::Other.into())
984 } else {
985 result
986 };
987
988 #[cfg(feature = "sqlite_test")]
989 let result = if simulate_diskfull_error() {
990 Err(ErrorKind::WriteZero.into())
991 } else {
992 result
993 };
994
995 match result {
996 Ok(_) => {}
997 Err(err) if err.kind() == ErrorKind::WriteZero => {
998 return ffi::SQLITE_FULL;
999 }
1000 Err(err) => return state.set_last_error(ffi::SQLITE_IOERR_WRITE, err),
1001 }
1002
1003 ffi::SQLITE_OK
1004 }
1005
1006 pub unsafe extern "C" fn truncate<V, F: DatabaseHandle>(
1008 p_file: *mut ffi::sqlite3_file,
1009 size: ffi::sqlite3_int64,
1010 ) -> c_int {
1011 let state = match file_state::<V, F>(p_file) {
1012 Ok(f) => f,
1013 Err(_) => return ffi::SQLITE_IOERR_FSYNC,
1014 };
1015
1016 let size: u64 = if let Some(chunk_size) = state.chunk_size {
1017 (((size as usize + chunk_size - 1) / chunk_size) * chunk_size) as u64
1018 } else {
1019 size as u64
1020 };
1021
1022 log::trace!("[{}] truncate size={} ({})", state.id, size, state.db_name);
1023
1024 if let Err(err) = state.file.set_len(size) {
1030 return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1031 }
1032
1033 ffi::SQLITE_OK
1034 }
1035
1036 pub unsafe extern "C" fn sync<V, F: DatabaseHandle>(
1038 p_file: *mut ffi::sqlite3_file,
1039 flags: c_int,
1040 ) -> c_int {
1041 let state = match file_state::<V, F>(p_file) {
1042 Ok(f) => f,
1043 Err(_) => return ffi::SQLITE_IOERR_FSYNC,
1044 };
1045 log::trace!("[{}] sync ({})", state.id, state.db_name);
1046
1047 #[cfg(feature = "sqlite_test")]
1048 {
1049 let is_full_sync = flags & 0x0F == ffi::SQLITE_SYNC_FULL;
1050 if is_full_sync {
1051 ffi::sqlite3_inc_fullsync_count();
1052 }
1053 ffi::sqlite3_inc_sync_count();
1054 }
1055
1056 if let Err(err) = state.file.sync(flags & ffi::SQLITE_SYNC_DATAONLY > 0) {
1057 return state.set_last_error(ffi::SQLITE_IOERR_FSYNC, err);
1058 }
1059
1060 ffi::SQLITE_OK
1066 }
1067
1068 pub unsafe extern "C" fn file_size<V, F: DatabaseHandle>(
1070 p_file: *mut ffi::sqlite3_file,
1071 p_size: *mut ffi::sqlite3_int64,
1072 ) -> c_int {
1073 let state = match file_state::<V, F>(p_file) {
1074 Ok(f) => f,
1075 Err(_) => return ffi::SQLITE_IOERR_FSTAT,
1076 };
1077 log::trace!("[{}] file_size ({})", state.id, state.db_name);
1078
1079 if let Err(err) = state.file.size().and_then(|n| {
1080 let p_size: &mut ffi::sqlite3_int64 = p_size.as_mut().ok_or_else(null_ptr_error)?;
1081 *p_size = n as ffi::sqlite3_int64;
1082 Ok(())
1083 }) {
1084 return state.set_last_error(ffi::SQLITE_IOERR_FSTAT, err);
1085 }
1086
1087 ffi::SQLITE_OK
1093 }
1094
1095 pub unsafe extern "C" fn lock<V, F: DatabaseHandle>(
1097 p_file: *mut ffi::sqlite3_file,
1098 e_lock: c_int,
1099 ) -> c_int {
1100 let state = match file_state::<V, F>(p_file) {
1101 Ok(f) => f,
1102 Err(_) => return ffi::SQLITE_IOERR_LOCK,
1103 };
1104 log::trace!("[{}] lock ({})", state.id, state.db_name);
1105
1106 let lock = match LockKind::from_i32(e_lock) {
1107 Some(lock) => lock,
1108 None => return ffi::SQLITE_IOERR_LOCK,
1109 };
1110 match state.file.lock(lock) {
1111 Ok(true) => {
1112 state.has_exclusive_lock = lock == LockKind::Exclusive;
1113 log::trace!("[{}] lock={:?} ({})", state.id, lock, state.db_name);
1114
1115 if state.has_exclusive_lock {
1118 let has_exclusive_wal_index = state
1119 .wal_index_locks
1120 .iter()
1121 .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1122
1123 if !has_exclusive_wal_index {
1124 log::trace!(
1125 "[{}] acquired exclusive db lock, pulling wal index changes",
1126 state.id,
1127 );
1128
1129 if let Some((wal_index, _)) = state.wal_index.as_mut() {
1130 for (region, data) in &mut state.wal_index_regions {
1131 if let Err(err) = wal_index.pull(*region as u32, data) {
1132 log::error!(
1133 "[{}] pulling wal index changes failed: {}",
1134 state.id,
1135 err
1136 )
1137 }
1138 }
1139 }
1140 }
1141 }
1142
1143 ffi::SQLITE_OK
1144 }
1145 Ok(false) => {
1146 log::trace!(
1147 "[{}] busy (denied {:?}) ({})",
1148 state.id,
1149 lock,
1150 state.db_name
1151 );
1152 ffi::SQLITE_BUSY
1153 }
1154 Err(err) => state.set_last_error(ffi::SQLITE_IOERR_LOCK, err),
1155 }
1156 }
1157
1158 pub unsafe extern "C" fn unlock<V, F: DatabaseHandle>(
1160 p_file: *mut ffi::sqlite3_file,
1161 e_lock: c_int,
1162 ) -> c_int {
1163 let state = match file_state::<V, F>(p_file) {
1164 Ok(f) => f,
1165 Err(_) => return ffi::SQLITE_IOERR_UNLOCK,
1166 };
1167 log::trace!("[{}] unlock ({})", state.id, state.db_name);
1168
1169 let lock = match LockKind::from_i32(e_lock) {
1170 Some(lock) => lock,
1171 None => return ffi::SQLITE_IOERR_UNLOCK,
1172 };
1173 match state.file.unlock(lock) {
1174 Ok(true) => {
1175 state.has_exclusive_lock = lock == LockKind::Exclusive;
1176 log::trace!("[{}] unlock={:?} ({})", state.id, lock, state.db_name);
1177 ffi::SQLITE_OK
1178 }
1179 Ok(false) => ffi::SQLITE_BUSY,
1180 Err(err) => state.set_last_error(ffi::SQLITE_IOERR_UNLOCK, err),
1181 }
1182 }
1183
1184 pub unsafe extern "C" fn check_reserved_lock<V, F: DatabaseHandle>(
1186 p_file: *mut ffi::sqlite3_file,
1187 p_res_out: *mut c_int,
1188 ) -> c_int {
1189 let state = match file_state::<V, F>(p_file) {
1190 Ok(f) => f,
1191 Err(_) => return ffi::SQLITE_IOERR_CHECKRESERVEDLOCK,
1192 };
1193 log::trace!("[{}] check_reserved_lock ({})", state.id, state.db_name);
1194
1195 if let Err(err) = state.file.reserved().and_then(|is_reserved| {
1201 let p_res_out: &mut c_int = p_res_out.as_mut().ok_or_else(null_ptr_error)?;
1202 *p_res_out = is_reserved as c_int;
1203 Ok(())
1204 }) {
1205 return state.set_last_error(ffi::SQLITE_IOERR_UNLOCK, err);
1206 }
1207
1208 ffi::SQLITE_OK
1209 }
1210
1211 pub unsafe extern "C" fn file_control<V: Vfs, F: DatabaseHandle>(
1213 p_file: *mut ffi::sqlite3_file,
1214 op: c_int,
1215 p_arg: *mut c_void,
1216 ) -> c_int {
1217 let state = match file_state::<V, F>(p_file) {
1218 Ok(f) => f,
1219 Err(_) => return ffi::SQLITE_NOTFOUND,
1220 };
1221 log::trace!("[{}] file_control op={} ({})", state.id, op, state.db_name);
1222
1223 match op {
1225 ffi::SQLITE_FCNTL_FILE_POINTER
1228 | ffi::SQLITE_FCNTL_VFS_POINTER
1229 | ffi::SQLITE_FCNTL_JOURNAL_POINTER
1230 | ffi::SQLITE_FCNTL_DATA_VERSION
1231 | ffi::SQLITE_FCNTL_RESERVE_BYTES => ffi::SQLITE_NOTFOUND,
1232
1233 ffi::SQLITE_FCNTL_SYNC_OMITTED => ffi::SQLITE_NOTFOUND,
1235
1236 ffi::SQLITE_FCNTL_LOCKSTATE => match state.file.current_lock() {
1238 Ok(lock) => {
1239 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1240 *p_arg = lock as i32;
1241 }
1242 ffi::SQLITE_OK
1243 }
1244 Err(err) => state.set_last_error(ffi::SQLITE_ERROR, err),
1245 },
1246
1247 ffi::SQLITE_FCNTL_GET_LOCKPROXYFILE | ffi::SQLITE_FCNTL_SET_LOCKPROXYFILE => {
1249 ffi::SQLITE_NOTFOUND
1250 }
1251
1252 ffi::SQLITE_FCNTL_LAST_ERRNO => {
1254 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1255 *p_arg = state.last_errno;
1256 }
1257 ffi::SQLITE_OK
1258 }
1259
1260 ffi::SQLITE_FCNTL_SIZE_HINT => {
1263 let size_hint = match (p_arg as *mut i64)
1264 .as_ref()
1265 .cloned()
1266 .and_then(|s| u64::try_from(s).ok())
1267 {
1268 Some(chunk_size) => chunk_size,
1269 None => {
1270 return state.set_last_error(
1271 ffi::SQLITE_NOTFOUND,
1272 std::io::Error::new(ErrorKind::Other, "expect size hint arg"),
1273 );
1274 }
1275 };
1276
1277 let current = match state.file.size() {
1281 Ok(size) => size,
1282 Err(err) => return state.set_last_error(ffi::SQLITE_ERROR, err),
1283 };
1284
1285 if current > size_hint {
1286 return ffi::SQLITE_OK;
1287 }
1288
1289 if let Some(chunk_size) = state.chunk_size {
1290 let chunk_size = chunk_size as u64;
1291 let size = ((size_hint + chunk_size - 1) / chunk_size) * chunk_size;
1292 if let Err(err) = state.file.set_len(size) {
1293 return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1294 }
1295 } else if let Err(err) = state.file.set_len(size_hint) {
1296 return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1297 }
1298
1299 ffi::SQLITE_OK
1305 }
1306
1307 ffi::SQLITE_FCNTL_CHUNK_SIZE => {
1311 let chunk_size = match (p_arg as *mut i32)
1312 .as_ref()
1313 .cloned()
1314 .and_then(|s| usize::try_from(s).ok())
1315 {
1316 Some(chunk_size) => chunk_size,
1317 None => {
1318 return state.set_last_error(
1319 ffi::SQLITE_NOTFOUND,
1320 std::io::Error::new(ErrorKind::Other, "expect chunk_size arg"),
1321 );
1322 }
1323 };
1324
1325 if let Err(err) = state.file.set_chunk_size(chunk_size) {
1326 return state.set_last_error(ffi::SQLITE_ERROR, err);
1327 }
1328
1329 state.chunk_size = Some(chunk_size);
1330
1331 ffi::SQLITE_OK
1332 }
1333
1334 ffi::SQLITE_FCNTL_WIN32_AV_RETRY => ffi::SQLITE_NOTFOUND,
1338
1339 ffi::SQLITE_FCNTL_PERSIST_WAL => {
1341 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1342 if *p_arg < 0 {
1343 *p_arg = state.persist_wal as i32;
1345 } else {
1346 state.persist_wal = *p_arg == 1;
1347 }
1348 };
1349
1350 ffi::SQLITE_OK
1351 }
1352
1353 ffi::SQLITE_FCNTL_OVERWRITE => ffi::SQLITE_NOTFOUND,
1356
1357 ffi::SQLITE_FCNTL_VFSNAME => {
1359 if let Some(p_arg) = (p_arg as *mut *const c_char).as_mut() {
1360 let name = ManuallyDrop::new(state.vfs_name.clone());
1361 *p_arg = name.as_ptr();
1362 };
1363
1364 ffi::SQLITE_OK
1365 }
1366
1367 ffi::SQLITE_FCNTL_POWERSAFE_OVERWRITE => {
1369 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1370 if *p_arg < 0 {
1371 *p_arg = state.powersafe_overwrite as i32;
1373 } else {
1374 state.powersafe_overwrite = *p_arg == 1;
1375 }
1376 };
1377
1378 ffi::SQLITE_OK
1379 }
1380
1381 ffi::SQLITE_FCNTL_PRAGMA => ffi::SQLITE_NOTFOUND,
1383
1384 ffi::SQLITE_FCNTL_BUSYHANDLER => ffi::SQLITE_NOTFOUND,
1388
1389 ffi::SQLITE_FCNTL_TEMPFILENAME => {
1391 if let Some(p_arg) = (p_arg as *mut *const c_char).as_mut() {
1392 let name = state.vfs.temporary_name();
1393 let name = CString::new(name.as_bytes()).unwrap();
1395 let name = ManuallyDrop::new(name);
1396 *p_arg = name.as_ptr();
1397 };
1398
1399 ffi::SQLITE_OK
1400 }
1401
1402 ffi::SQLITE_FCNTL_MMAP_SIZE => ffi::SQLITE_NOTFOUND,
1405
1406 ffi::SQLITE_FCNTL_TRACE => {
1409 let trace = CStr::from_ptr(p_arg as *const c_char);
1410 log::trace!("{}", trace.to_string_lossy());
1411 ffi::SQLITE_OK
1412 }
1413
1414 ffi::SQLITE_FCNTL_HAS_MOVED => match state.file.moved() {
1417 Ok(moved) => {
1418 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1419 *p_arg = moved as i32;
1420 }
1421 ffi::SQLITE_OK
1422 }
1423 Err(err) => state.set_last_error(ffi::SQLITE_ERROR, err),
1424 },
1425
1426 ffi::SQLITE_FCNTL_SYNC => ffi::SQLITE_OK,
1429
1430 ffi::SQLITE_FCNTL_COMMIT_PHASETWO => ffi::SQLITE_OK,
1433
1434 ffi::SQLITE_FCNTL_WIN32_SET_HANDLE => ffi::SQLITE_NOTFOUND,
1438
1439 ffi::SQLITE_FCNTL_WAL_BLOCK => ffi::SQLITE_NOTFOUND,
1444
1445 ffi::SQLITE_FCNTL_ZIPVFS => ffi::SQLITE_NOTFOUND,
1447
1448 ffi::SQLITE_FCNTL_RBU => ffi::SQLITE_NOTFOUND,
1450
1451 ffi::SQLITE_FCNTL_WIN32_GET_HANDLE => ffi::SQLITE_NOTFOUND,
1454
1455 ffi::SQLITE_FCNTL_PDB => ffi::SQLITE_NOTFOUND,
1457
1458 ffi::SQLITE_FCNTL_BEGIN_ATOMIC_WRITE
1460 | ffi::SQLITE_FCNTL_COMMIT_ATOMIC_WRITE
1461 | ffi::SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE => ffi::SQLITE_NOTFOUND,
1462
1463 ffi::SQLITE_FCNTL_LOCK_TIMEOUT => ffi::SQLITE_NOTFOUND,
1466
1467 ffi::SQLITE_FCNTL_SIZE_LIMIT => ffi::SQLITE_NOTFOUND,
1469
1470 ffi::SQLITE_FCNTL_CKPT_DONE => ffi::SQLITE_OK,
1474
1475 ffi::SQLITE_FCNTL_CKPT_START => ffi::SQLITE_OK,
1478
1479 ffi::SQLITE_FCNTL_EXTERNAL_READER => ffi::SQLITE_NOTFOUND,
1483
1484 ffi::SQLITE_FCNTL_CKSM_FILE => ffi::SQLITE_NOTFOUND,
1486
1487 _ => ffi::SQLITE_NOTFOUND,
1488 }
1489 }
1490
1491 pub unsafe extern "C" fn sector_size<F>(_p_file: *mut ffi::sqlite3_file) -> c_int {
1493 log::trace!("sector_size");
1494
1495 1024
1496 }
1497
1498 pub unsafe extern "C" fn device_characteristics<V, F: DatabaseHandle>(
1500 p_file: *mut ffi::sqlite3_file,
1501 ) -> c_int {
1502 let state = match file_state::<V, F>(p_file) {
1503 Ok(f) => f,
1504 Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1505 };
1506
1507 log::trace!("[{}] device_characteristics", state.id,);
1508
1509 if state.powersafe_overwrite {
1515 ffi::SQLITE_IOCAP_POWERSAFE_OVERWRITE
1516 } else {
1517 0
1518 }
1519 }
1520
1521 pub unsafe extern "C" fn shm_map<V, F: DatabaseHandle>(
1523 p_file: *mut ffi::sqlite3_file,
1524 region_ix: i32,
1525 region_size: i32,
1526 b_extend: i32,
1527 pp: *mut *mut c_void,
1528 ) -> i32 {
1529 let state = match file_state::<V, F>(p_file) {
1530 Ok(f) => f,
1531 Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1532 };
1533 log::trace!(
1534 "[{}] shm_map pg={} sz={} extend={} ({})",
1535 state.id,
1536 region_ix,
1537 region_size,
1538 b_extend,
1539 state.db_name
1540 );
1541
1542 if !F::WalIndex::enabled() {
1543 return ffi::SQLITE_IOERR_SHMLOCK;
1544 }
1545
1546 if region_size != 32768 {
1547 return state.set_last_error(
1548 ffi::SQLITE_IOERR_SHMMAP,
1549 std::io::Error::new(
1550 ErrorKind::Other,
1551 format!(
1552 "encountered region size other than 32kB; got {}",
1553 region_size
1554 ),
1555 ),
1556 );
1557 }
1558
1559 let (wal_index, readonly) = match state.wal_index.as_mut() {
1560 Some((wal_index, readonly)) => (wal_index, *readonly),
1561 None => {
1562 let (wal_index, readonly) = state.wal_index.get_or_insert(
1563 match state
1564 .file
1565 .wal_index(false)
1566 .map(|wal_index| (wal_index, false))
1567 .or_else(|err| {
1568 if err.kind() == ErrorKind::PermissionDenied {
1569 state
1571 .file
1572 .wal_index(true)
1573 .map(|wal_index| (wal_index, true))
1574 .map_err(|_| err)
1575 } else {
1576 Err(err)
1577 }
1578 }) {
1579 Ok((wal_index, readonly)) => (wal_index, readonly),
1580 Err(err) => {
1581 return state.set_last_error(ffi::SQLITE_IOERR_SHMMAP, err);
1582 }
1583 },
1584 );
1585 (wal_index, *readonly)
1586 }
1587 };
1588
1589 let entry = state.wal_index_regions.entry(region_ix as u32);
1590 match entry {
1591 Entry::Occupied(mut entry) => {
1592 *pp = entry.get_mut().as_mut_ptr() as *mut c_void;
1593 }
1594 Entry::Vacant(entry) => {
1595 let mut m = match wal_index.map(region_ix as u32) {
1596 Ok(m) => Box::pin(m),
1597 Err(err) => {
1598 return state.set_last_error(ffi::SQLITE_IOERR_SHMMAP, err);
1599 }
1600 };
1601 *pp = m.as_mut_ptr() as *mut c_void;
1602 entry.insert(m);
1603 }
1604 }
1605
1606 if readonly {
1607 ffi::SQLITE_READONLY
1608 } else {
1609 ffi::SQLITE_OK
1610 }
1611 }
1612
1613 pub unsafe extern "C" fn shm_lock<V, F: DatabaseHandle>(
1615 p_file: *mut ffi::sqlite3_file,
1616 offset: i32,
1617 n: i32,
1618 flags: i32,
1619 ) -> i32 {
1620 let state = match file_state::<V, F>(p_file) {
1621 Ok(f) => f,
1622 Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1623 };
1624 let locking = flags & ffi::SQLITE_SHM_LOCK > 0;
1625 let exclusive = flags & ffi::SQLITE_SHM_EXCLUSIVE > 0;
1626 log::trace!(
1627 "[{}] shm_lock offset={} n={} lock={} exclusive={} (flags={}) ({})",
1628 state.id,
1629 offset,
1630 n,
1631 locking,
1632 exclusive,
1633 flags,
1634 state.db_name
1635 );
1636
1637 let range = offset as u8..(offset + n) as u8;
1638 let lock = match (locking, exclusive) {
1639 (true, true) => wip::WalIndexLock::Exclusive,
1640 (true, false) => wip::WalIndexLock::Shared,
1641 (false, _) => wip::WalIndexLock::None,
1642 };
1643
1644 let (wal_index, readonly) = match state.wal_index.as_mut() {
1645 Some((wal_index, readonly)) => (wal_index, *readonly),
1646 None => {
1647 return state.set_last_error(
1648 ffi::SQLITE_IOERR_SHMLOCK,
1649 std::io::Error::new(
1650 ErrorKind::Other,
1651 "trying to lock wal index, which isn't created yet",
1652 ),
1653 )
1654 }
1655 };
1656
1657 if locking {
1658 let has_exclusive = state
1659 .wal_index_locks
1660 .iter()
1661 .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1662
1663 if !has_exclusive {
1664 log::trace!(
1665 "[{}] does not have wal index write lock, pulling changes",
1666 state.id
1667 );
1668 for (region, data) in &mut state.wal_index_regions {
1669 if let Err(err) = wal_index.pull(*region as u32, data) {
1670 return state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err);
1671 }
1672 }
1673 }
1674 } else {
1675 let releases_any_exclusive = state.wal_index_locks.iter().any(|(region, lock)| {
1676 *lock == wip::WalIndexLock::Exclusive && range.contains(region)
1677 });
1678
1679 if releases_any_exclusive && !readonly {
1681 log::trace!(
1682 "[{}] releasing an exclusive lock, pushing wal index changes",
1683 state.id,
1684 );
1685 for (region, data) in &mut state.wal_index_regions {
1686 if let Err(err) = wal_index.push(*region as u32, data) {
1687 return state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err);
1688 }
1689 }
1690 }
1691 }
1692
1693 match wal_index.lock(range.clone(), lock) {
1694 Ok(true) => {
1695 for region in range {
1696 state.wal_index_locks.insert(region, lock);
1697 }
1698 ffi::SQLITE_OK
1699 }
1700 Ok(false) => ffi::SQLITE_BUSY,
1701 Err(err) => state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err),
1702 }
1703 }
1704
1705 pub unsafe extern "C" fn shm_barrier<V, F: DatabaseHandle>(p_file: *mut ffi::sqlite3_file) {
1707 let state = match file_state::<V, F>(p_file) {
1708 Ok(f) => f,
1709 Err(_) => return,
1710 };
1711 log::trace!("[{}] shm_barrier ({})", state.id, state.db_name);
1712
1713 let (wal_index, readonly) = if let Some((wal_index, readonly)) = state.wal_index.as_mut() {
1714 (wal_index, *readonly)
1715 } else {
1716 return;
1717 };
1718
1719 if state.has_exclusive_lock && !readonly {
1720 log::trace!(
1721 "[{}] has exclusive db lock, pushing wal index changes",
1722 state.id,
1723 );
1724 for (region, data) in &mut state.wal_index_regions {
1725 if let Err(err) = wal_index.push(*region as u32, data) {
1726 log::error!("[{}] pushing wal index changes failed: {}", state.id, err)
1727 }
1728 }
1729
1730 return;
1731 }
1732
1733 let has_exclusive = state
1734 .wal_index_locks
1735 .iter()
1736 .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1737
1738 if !has_exclusive {
1739 log::trace!(
1740 "[{}] does not have wal index write lock, pulling changes",
1741 state.id
1742 );
1743 for (region, data) in &mut state.wal_index_regions {
1744 if let Err(err) = wal_index.pull(*region as u32, data) {
1745 log::error!("[{}] pulling wal index changes failed: {}", state.id, err)
1746 }
1747 }
1748 }
1749 }
1750
1751 pub unsafe extern "C" fn shm_unmap<V, F: DatabaseHandle>(
1753 p_file: *mut ffi::sqlite3_file,
1754 delete_flags: i32,
1755 ) -> i32 {
1756 let state = match file_state::<V, F>(p_file) {
1757 Ok(f) => f,
1758 Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1759 };
1760 log::trace!(
1761 "[{}] shm_unmap delete={} ({})",
1762 state.id,
1763 delete_flags == 1,
1764 state.db_name
1765 );
1766
1767 state.wal_index_regions.clear();
1768 state.wal_index_locks.clear();
1769
1770 if delete_flags == 1 {
1771 if let Some((wal_index, readonly)) = state.wal_index.take() {
1772 if !readonly {
1773 if let Err(err) = wal_index.delete() {
1774 return state.set_last_error(ffi::SQLITE_ERROR, err);
1775 }
1776 }
1777 }
1778 }
1779
1780 ffi::SQLITE_OK
1781 }
1782}
1783
1784#[cfg(feature = "sqlite_test")]
1806#[inline]
1807unsafe fn simulate_io_error() -> bool {
1808 if (ffi::sqlite3_get_io_error_persist() != 0 && ffi::sqlite3_get_io_error_hit() != 0)
1809 || ffi::sqlite3_dec_io_error_pending() == 1
1810 {
1811 ffi::sqlite3_inc_io_error_hit();
1812 if ffi::sqlite3_get_io_error_benign() == 0 {
1813 ffi::sqlite3_inc_io_error_hardhit();
1814 }
1815
1816 return true;
1817 }
1818
1819 false
1820}
1821
1822#[cfg(feature = "sqlite_test")]
1823#[inline]
1824unsafe fn simulate_diskfull_error() -> bool {
1825 if ffi::sqlite3_get_diskfull_pending() != 0 {
1826 if ffi::sqlite3_get_diskfull_pending() == 1 {
1827 if ffi::sqlite3_get_io_error_benign() == 0 {
1828 ffi::sqlite3_inc_io_error_hardhit();
1829 }
1830 ffi::sqlite3_set_diskfull();
1831 ffi::sqlite3_set_io_error_hit(1);
1832 return true;
1833 } else {
1834 ffi::sqlite3_dec_diskfull_pending();
1835 }
1836 }
1837
1838 false
1839}
1840
1841impl<V> State<V> {
1842 fn set_last_error(&mut self, no: i32, err: std::io::Error) -> i32 {
1843 *(self.last_error.lock().unwrap()) = Some((no, err));
1845 no
1846 }
1847}
1848
1849impl<V, F: DatabaseHandle> FileExt<V, F> {
1850 fn set_last_error(&mut self, no: i32, err: std::io::Error) -> i32 {
1851 *(self.last_error.lock().unwrap()) = Some((no, err));
1853 self.last_errno = no;
1854 no
1855 }
1856}
1857
1858fn null_ptr_error() -> std::io::Error {
1859 std::io::Error::new(ErrorKind::Other, "received null pointer")
1860}
1861
1862unsafe fn vfs_state<'a, V>(ptr: *mut ffi::sqlite3_vfs) -> Result<&'a mut State<V>, std::io::Error> {
1863 let vfs: &mut ffi::sqlite3_vfs = ptr.as_mut().ok_or_else(null_ptr_error)?;
1864 let state = (vfs.pAppData as *mut State<V>)
1865 .as_mut()
1866 .ok_or_else(null_ptr_error)?;
1867 Ok(state)
1868}
1869
1870unsafe fn file_state<'a, V, F: DatabaseHandle>(
1871 ptr: *mut ffi::sqlite3_file,
1872) -> Result<&'a mut FileExt<V, F>, std::io::Error> {
1873 let f = (ptr as *mut FileState<V, F>)
1874 .as_mut()
1875 .ok_or_else(null_ptr_error)?;
1876 let ext = f.ext.assume_init_mut();
1877 Ok(ext)
1878}
1879
1880impl OpenOptions {
1881 fn from_flags(flags: i32) -> Option<Self> {
1882 Some(OpenOptions {
1883 kind: OpenKind::from_flags(flags)?,
1884 access: OpenAccess::from_flags(flags)?,
1885 delete_on_close: flags & ffi::SQLITE_OPEN_DELETEONCLOSE > 0,
1886 })
1887 }
1888
1889 fn to_flags(&self) -> i32 {
1890 self.kind.to_flags()
1891 | self.access.to_flags()
1892 | if self.delete_on_close {
1893 ffi::SQLITE_OPEN_DELETEONCLOSE
1894 } else {
1895 0
1896 }
1897 }
1898}
1899
1900impl OpenKind {
1901 fn from_flags(flags: i32) -> Option<Self> {
1902 match flags {
1903 flags if flags & ffi::SQLITE_OPEN_MAIN_DB > 0 => Some(Self::MainDb),
1904 flags if flags & ffi::SQLITE_OPEN_MAIN_JOURNAL > 0 => Some(Self::MainJournal),
1905 flags if flags & ffi::SQLITE_OPEN_TEMP_DB > 0 => Some(Self::TempDb),
1906 flags if flags & ffi::SQLITE_OPEN_TEMP_JOURNAL > 0 => Some(Self::TempJournal),
1907 flags if flags & ffi::SQLITE_OPEN_TRANSIENT_DB > 0 => Some(Self::TransientDb),
1908 flags if flags & ffi::SQLITE_OPEN_SUBJOURNAL > 0 => Some(Self::SubJournal),
1909 flags if flags & ffi::SQLITE_OPEN_SUPER_JOURNAL > 0 => Some(Self::SuperJournal),
1910 flags if flags & ffi::SQLITE_OPEN_WAL > 0 => Some(Self::Wal),
1911 _ => None,
1912 }
1913 }
1914
1915 fn to_flags(self) -> i32 {
1916 match self {
1917 OpenKind::MainDb => ffi::SQLITE_OPEN_MAIN_DB,
1918 OpenKind::MainJournal => ffi::SQLITE_OPEN_MAIN_JOURNAL,
1919 OpenKind::TempDb => ffi::SQLITE_OPEN_TEMP_DB,
1920 OpenKind::TempJournal => ffi::SQLITE_OPEN_TEMP_JOURNAL,
1921 OpenKind::TransientDb => ffi::SQLITE_OPEN_TRANSIENT_DB,
1922 OpenKind::SubJournal => ffi::SQLITE_OPEN_SUBJOURNAL,
1923 OpenKind::SuperJournal => ffi::SQLITE_OPEN_SUPER_JOURNAL,
1924 OpenKind::Wal => ffi::SQLITE_OPEN_WAL,
1925 }
1926 }
1927}
1928
1929impl OpenAccess {
1930 fn from_flags(flags: i32) -> Option<Self> {
1931 match flags {
1932 flags
1933 if (flags & ffi::SQLITE_OPEN_CREATE > 0)
1934 && (flags & ffi::SQLITE_OPEN_EXCLUSIVE > 0) =>
1935 {
1936 Some(Self::CreateNew)
1937 }
1938 flags if flags & ffi::SQLITE_OPEN_CREATE > 0 => Some(Self::Create),
1939 flags if flags & ffi::SQLITE_OPEN_READWRITE > 0 => Some(Self::Write),
1940 flags if flags & ffi::SQLITE_OPEN_READONLY > 0 => Some(Self::Read),
1941 _ => None,
1942 }
1943 }
1944
1945 fn to_flags(self) -> i32 {
1946 match self {
1947 OpenAccess::Read => ffi::SQLITE_OPEN_READONLY,
1948 OpenAccess::Write => ffi::SQLITE_OPEN_READWRITE,
1949 OpenAccess::Create => ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE,
1950 OpenAccess::CreateNew => {
1951 ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_EXCLUSIVE
1952 }
1953 }
1954 }
1955}
1956
1957impl LockKind {
1958 fn from_i32(lock: i32) -> Option<Self> {
1959 Some(match lock {
1960 ffi::SQLITE_LOCK_NONE => Self::None,
1961 ffi::SQLITE_LOCK_SHARED => Self::Shared,
1962 ffi::SQLITE_LOCK_RESERVED => Self::Reserved,
1963 ffi::SQLITE_LOCK_PENDING => Self::Pending,
1964 ffi::SQLITE_LOCK_EXCLUSIVE => Self::Exclusive,
1965 _ => return None,
1966 })
1967 }
1968
1969 fn to_i32(self) -> i32 {
1970 match self {
1971 Self::None => ffi::SQLITE_LOCK_NONE,
1972 Self::Shared => ffi::SQLITE_LOCK_SHARED,
1973 Self::Reserved => ffi::SQLITE_LOCK_RESERVED,
1974 Self::Pending => ffi::SQLITE_LOCK_PENDING,
1975 Self::Exclusive => ffi::SQLITE_LOCK_EXCLUSIVE,
1976 }
1977 }
1978}
1979
1980impl PartialOrd for LockKind {
1981 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1982 self.to_i32().partial_cmp(&other.to_i32())
1983 }
1984}
1985
1986impl Default for LockKind {
1987 fn default() -> Self {
1988 Self::None
1989 }
1990}
1991
1992#[derive(Default)]
1993pub struct WalDisabled;
1994
1995impl wip::WalIndex for WalDisabled {
1996 fn enabled() -> bool {
1997 false
1998 }
1999
2000 fn map(&mut self, _region: u32) -> Result<[u8; 32768], std::io::Error> {
2001 Err(std::io::Error::new(ErrorKind::Other, "wal is disabled"))
2002 }
2003
2004 fn lock(
2005 &mut self,
2006 _locks: Range<u8>,
2007 _lock: wip::WalIndexLock,
2008 ) -> Result<bool, std::io::Error> {
2009 Err(std::io::Error::new(ErrorKind::Other, "wal is disabled"))
2010 }
2011
2012 fn delete(self) -> Result<(), std::io::Error> {
2013 Ok(())
2014 }
2015}
2016
2017#[derive(Debug)]
2018pub enum RegisterError {
2019 Nul(std::ffi::NulError),
2020 Register(i32),
2021}
2022
2023impl std::error::Error for RegisterError {
2024 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
2025 match self {
2026 Self::Nul(err) => Some(err),
2027 Self::Register(_) => None,
2028 }
2029 }
2030}
2031
2032impl std::fmt::Display for RegisterError {
2033 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2034 match self {
2035 Self::Nul(_) => f.write_str("interior nul byte in name found"),
2036 Self::Register(code) => {
2037 write!(f, "registering sqlite vfs failed with error code: {}", code)
2038 }
2039 }
2040 }
2041}
2042
2043impl From<std::ffi::NulError> for RegisterError {
2044 fn from(err: std::ffi::NulError) -> Self {
2045 Self::Nul(err)
2046 }
2047}
2048
2049#[cfg(test)]
2050mod tests {
2051 use super::*;
2052
2053 #[test]
2054 fn test_lock_order() {
2055 assert!(LockKind::None < LockKind::Shared);
2056 assert!(LockKind::Shared < LockKind::Reserved);
2057 assert!(LockKind::Reserved < LockKind::Pending);
2058 assert!(LockKind::Pending < LockKind::Exclusive);
2059 }
2060}