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 = time::OffsetDateTime::now_utc().unix_timestamp() + UNIX_EPOCH;
802 #[cfg(feature = "sqlite_test")]
803 let now = if ffi::sqlite3_get_current_time() > 0 {
804 ffi::sqlite3_get_current_time() as i64 * 1000 + UNIX_EPOCH
805 } else {
806 now
807 };
808
809 *p = now;
810 ffi::SQLITE_OK
811 }
812
813 #[cfg(feature = "syscall")]
814 pub unsafe extern "C" fn set_system_call<V>(
815 p_vfs: *mut ffi::sqlite3_vfs,
816 z_name: *const ::std::os::raw::c_char,
817 p_new_func: ffi::sqlite3_syscall_ptr,
818 ) -> ::std::os::raw::c_int {
819 let state = match vfs_state::<V>(p_vfs) {
820 Ok(state) => state,
821 Err(_) => return ffi::SQLITE_ERROR,
822 };
823
824 if let Some(set_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xSetSystemCall) {
825 return set_system_call(state.parent_vfs, z_name, p_new_func);
826 }
827
828 ffi::SQLITE_ERROR
829 }
830
831 #[cfg(feature = "syscall")]
832 pub unsafe extern "C" fn get_system_call<V>(
833 p_vfs: *mut ffi::sqlite3_vfs,
834 z_name: *const ::std::os::raw::c_char,
835 ) -> ffi::sqlite3_syscall_ptr {
836 let state = match vfs_state::<V>(p_vfs) {
837 Ok(state) => state,
838 Err(_) => return None,
839 };
840
841 if let Some(get_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xGetSystemCall) {
842 return get_system_call(state.parent_vfs, z_name);
843 }
844
845 None
846 }
847
848 #[cfg(feature = "syscall")]
849 pub unsafe extern "C" fn next_system_call<V>(
850 p_vfs: *mut ffi::sqlite3_vfs,
851 z_name: *const ::std::os::raw::c_char,
852 ) -> *const ::std::os::raw::c_char {
853 let state = match vfs_state::<V>(p_vfs) {
854 Ok(state) => state,
855 Err(_) => return std::ptr::null(),
856 };
857
858 if let Some(next_system_call) = state.parent_vfs.as_ref().and_then(|v| v.xNextSystemCall) {
859 return next_system_call(state.parent_vfs, z_name);
860 }
861
862 std::ptr::null()
863 }
864
865 pub unsafe extern "C" fn get_last_error<V>(
866 p_vfs: *mut ffi::sqlite3_vfs,
867 n_byte: c_int,
868 z_err_msg: *mut c_char,
869 ) -> c_int {
870 let state = match vfs_state::<V>(p_vfs) {
871 Ok(state) => state,
872 Err(_) => return ffi::SQLITE_ERROR,
873 };
874 if let Some((eno, err)) = state.last_error.lock().unwrap().as_ref() {
875 let msg = match CString::new(err.to_string()) {
876 Ok(msg) => msg,
877 Err(_) => return ffi::SQLITE_ERROR,
878 };
879
880 let msg = msg.to_bytes_with_nul();
881 if msg.len() > n_byte as usize {
882 return ffi::SQLITE_ERROR;
883 }
884 let out = slice::from_raw_parts_mut(z_err_msg as *mut u8, msg.len());
885 out.copy_from_slice(msg);
886
887 return *eno;
888 }
889 ffi::SQLITE_OK
890 }
891}
892
893mod io {
894 use std::collections::hash_map::Entry;
895 use std::mem;
896
897 use super::*;
898 use wip::WalIndex;
899
900 pub unsafe extern "C" fn close<V: Vfs, F: DatabaseHandle>(
902 p_file: *mut ffi::sqlite3_file,
903 ) -> c_int {
904 if let Some(f) = (p_file as *mut FileState<V, F>).as_mut() {
905 let ext = f.ext.assume_init_mut();
906 if ext.delete_on_close {
907 if let Err(err) = ext.vfs.delete(&ext.db_name) {
908 return ext.set_last_error(ffi::SQLITE_DELETE, err);
909 }
910 }
911
912 let ext = mem::replace(&mut f.ext, MaybeUninit::uninit());
913 let ext = ext.assume_init(); log::trace!("[{}] close ({})", ext.id, ext.db_name);
915 }
916
917 #[cfg(feature = "sqlite_test")]
918 ffi::sqlite3_dec_open_file_count();
919
920 ffi::SQLITE_OK
921 }
922
923 pub unsafe extern "C" fn read<V, F: DatabaseHandle>(
925 p_file: *mut ffi::sqlite3_file,
926 z_buf: *mut c_void,
927 i_amt: c_int,
928 i_ofst: ffi::sqlite3_int64,
929 ) -> c_int {
930 let state = match file_state::<V, F>(p_file) {
931 Ok(f) => f,
932 Err(_) => return ffi::SQLITE_IOERR_CLOSE,
933 };
934 log::trace!(
935 "[{}] read offset={} len={} ({})",
936 state.id,
937 i_ofst,
938 i_amt,
939 state.db_name
940 );
941
942 let out = slice::from_raw_parts_mut(z_buf as *mut u8, i_amt as usize);
943 if let Err(err) = state.file.read_exact_at(out, i_ofst as u64) {
944 let kind = err.kind();
945 if kind == ErrorKind::UnexpectedEof {
946 return ffi::SQLITE_IOERR_SHORT_READ;
947 } else {
948 return state.set_last_error(ffi::SQLITE_IOERR_READ, err);
949 }
950 }
951
952 ffi::SQLITE_OK
953 }
954
955 pub unsafe extern "C" fn write<V, F: DatabaseHandle>(
957 p_file: *mut ffi::sqlite3_file,
958 z: *const c_void,
959 i_amt: c_int,
960 i_ofst: ffi::sqlite3_int64,
961 ) -> c_int {
962 let state = match file_state::<V, F>(p_file) {
963 Ok(f) => f,
964 Err(_) => return ffi::SQLITE_IOERR_WRITE,
965 };
966 log::trace!(
967 "[{}] write offset={} len={} ({})",
968 state.id,
969 i_ofst,
970 i_amt,
971 state.db_name
972 );
973
974 let data = slice::from_raw_parts(z as *mut u8, i_amt as usize);
975 let result = state.file.write_all_at(data, i_ofst as u64);
976
977 #[cfg(feature = "sqlite_test")]
978 let result = if simulate_io_error() {
979 Err(ErrorKind::Other.into())
980 } else {
981 result
982 };
983
984 #[cfg(feature = "sqlite_test")]
985 let result = if simulate_diskfull_error() {
986 Err(ErrorKind::WriteZero.into())
987 } else {
988 result
989 };
990
991 match result {
992 Ok(_) => {}
993 Err(err) if err.kind() == ErrorKind::WriteZero => {
994 return ffi::SQLITE_FULL;
995 }
996 Err(err) => return state.set_last_error(ffi::SQLITE_IOERR_WRITE, err),
997 }
998
999 ffi::SQLITE_OK
1000 }
1001
1002 pub unsafe extern "C" fn truncate<V, F: DatabaseHandle>(
1004 p_file: *mut ffi::sqlite3_file,
1005 size: ffi::sqlite3_int64,
1006 ) -> c_int {
1007 let state = match file_state::<V, F>(p_file) {
1008 Ok(f) => f,
1009 Err(_) => return ffi::SQLITE_IOERR_FSYNC,
1010 };
1011
1012 let size: u64 = if let Some(chunk_size) = state.chunk_size {
1013 (((size as usize + chunk_size - 1) / chunk_size) * chunk_size) as u64
1014 } else {
1015 size as u64
1016 };
1017
1018 log::trace!("[{}] truncate size={} ({})", state.id, size, state.db_name);
1019
1020 if let Err(err) = state.file.set_len(size) {
1026 return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1027 }
1028
1029 ffi::SQLITE_OK
1030 }
1031
1032 pub unsafe extern "C" fn sync<V, F: DatabaseHandle>(
1034 p_file: *mut ffi::sqlite3_file,
1035 flags: c_int,
1036 ) -> c_int {
1037 let state = match file_state::<V, F>(p_file) {
1038 Ok(f) => f,
1039 Err(_) => return ffi::SQLITE_IOERR_FSYNC,
1040 };
1041 log::trace!("[{}] sync ({})", state.id, state.db_name);
1042
1043 #[cfg(feature = "sqlite_test")]
1044 {
1045 let is_full_sync = flags & 0x0F == ffi::SQLITE_SYNC_FULL;
1046 if is_full_sync {
1047 ffi::sqlite3_inc_fullsync_count();
1048 }
1049 ffi::sqlite3_inc_sync_count();
1050 }
1051
1052 if let Err(err) = state.file.sync(flags & ffi::SQLITE_SYNC_DATAONLY > 0) {
1053 return state.set_last_error(ffi::SQLITE_IOERR_FSYNC, err);
1054 }
1055
1056 ffi::SQLITE_OK
1062 }
1063
1064 pub unsafe extern "C" fn file_size<V, F: DatabaseHandle>(
1066 p_file: *mut ffi::sqlite3_file,
1067 p_size: *mut ffi::sqlite3_int64,
1068 ) -> c_int {
1069 let state = match file_state::<V, F>(p_file) {
1070 Ok(f) => f,
1071 Err(_) => return ffi::SQLITE_IOERR_FSTAT,
1072 };
1073 log::trace!("[{}] file_size ({})", state.id, state.db_name);
1074
1075 if let Err(err) = state.file.size().and_then(|n| {
1076 let p_size: &mut ffi::sqlite3_int64 = p_size.as_mut().ok_or_else(null_ptr_error)?;
1077 *p_size = n as ffi::sqlite3_int64;
1078 Ok(())
1079 }) {
1080 return state.set_last_error(ffi::SQLITE_IOERR_FSTAT, err);
1081 }
1082
1083 ffi::SQLITE_OK
1089 }
1090
1091 pub unsafe extern "C" fn lock<V, F: DatabaseHandle>(
1093 p_file: *mut ffi::sqlite3_file,
1094 e_lock: c_int,
1095 ) -> c_int {
1096 let state = match file_state::<V, F>(p_file) {
1097 Ok(f) => f,
1098 Err(_) => return ffi::SQLITE_IOERR_LOCK,
1099 };
1100 log::trace!("[{}] lock ({})", state.id, state.db_name);
1101
1102 let lock = match LockKind::from_i32(e_lock) {
1103 Some(lock) => lock,
1104 None => return ffi::SQLITE_IOERR_LOCK,
1105 };
1106 match state.file.lock(lock) {
1107 Ok(true) => {
1108 state.has_exclusive_lock = lock == LockKind::Exclusive;
1109 log::trace!("[{}] lock={:?} ({})", state.id, lock, state.db_name);
1110
1111 if state.has_exclusive_lock {
1114 let has_exclusive_wal_index = state
1115 .wal_index_locks
1116 .iter()
1117 .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1118
1119 if !has_exclusive_wal_index {
1120 log::trace!(
1121 "[{}] acquired exclusive db lock, pulling wal index changes",
1122 state.id,
1123 );
1124
1125 if let Some((wal_index, _)) = state.wal_index.as_mut() {
1126 for (region, data) in &mut state.wal_index_regions {
1127 if let Err(err) = wal_index.pull(*region as u32, data) {
1128 log::error!(
1129 "[{}] pulling wal index changes failed: {}",
1130 state.id,
1131 err
1132 )
1133 }
1134 }
1135 }
1136 }
1137 }
1138
1139 ffi::SQLITE_OK
1140 }
1141 Ok(false) => {
1142 log::trace!(
1143 "[{}] busy (denied {:?}) ({})",
1144 state.id,
1145 lock,
1146 state.db_name
1147 );
1148 ffi::SQLITE_BUSY
1149 }
1150 Err(err) => state.set_last_error(ffi::SQLITE_IOERR_LOCK, err),
1151 }
1152 }
1153
1154 pub unsafe extern "C" fn unlock<V, F: DatabaseHandle>(
1156 p_file: *mut ffi::sqlite3_file,
1157 e_lock: c_int,
1158 ) -> c_int {
1159 let state = match file_state::<V, F>(p_file) {
1160 Ok(f) => f,
1161 Err(_) => return ffi::SQLITE_IOERR_UNLOCK,
1162 };
1163 log::trace!("[{}] unlock ({})", state.id, state.db_name);
1164
1165 let lock = match LockKind::from_i32(e_lock) {
1166 Some(lock) => lock,
1167 None => return ffi::SQLITE_IOERR_UNLOCK,
1168 };
1169 match state.file.unlock(lock) {
1170 Ok(true) => {
1171 state.has_exclusive_lock = lock == LockKind::Exclusive;
1172 log::trace!("[{}] unlock={:?} ({})", state.id, lock, state.db_name);
1173 ffi::SQLITE_OK
1174 }
1175 Ok(false) => ffi::SQLITE_BUSY,
1176 Err(err) => state.set_last_error(ffi::SQLITE_IOERR_UNLOCK, err),
1177 }
1178 }
1179
1180 pub unsafe extern "C" fn check_reserved_lock<V, F: DatabaseHandle>(
1182 p_file: *mut ffi::sqlite3_file,
1183 p_res_out: *mut c_int,
1184 ) -> c_int {
1185 let state = match file_state::<V, F>(p_file) {
1186 Ok(f) => f,
1187 Err(_) => return ffi::SQLITE_IOERR_CHECKRESERVEDLOCK,
1188 };
1189 log::trace!("[{}] check_reserved_lock ({})", state.id, state.db_name);
1190
1191 if let Err(err) = state.file.reserved().and_then(|is_reserved| {
1197 let p_res_out: &mut c_int = p_res_out.as_mut().ok_or_else(null_ptr_error)?;
1198 *p_res_out = is_reserved as c_int;
1199 Ok(())
1200 }) {
1201 return state.set_last_error(ffi::SQLITE_IOERR_UNLOCK, err);
1202 }
1203
1204 ffi::SQLITE_OK
1205 }
1206
1207 pub unsafe extern "C" fn file_control<V: Vfs, F: DatabaseHandle>(
1209 p_file: *mut ffi::sqlite3_file,
1210 op: c_int,
1211 p_arg: *mut c_void,
1212 ) -> c_int {
1213 let state = match file_state::<V, F>(p_file) {
1214 Ok(f) => f,
1215 Err(_) => return ffi::SQLITE_NOTFOUND,
1216 };
1217 log::trace!("[{}] file_control op={} ({})", state.id, op, state.db_name);
1218
1219 match op {
1221 ffi::SQLITE_FCNTL_FILE_POINTER
1224 | ffi::SQLITE_FCNTL_VFS_POINTER
1225 | ffi::SQLITE_FCNTL_JOURNAL_POINTER
1226 | ffi::SQLITE_FCNTL_DATA_VERSION
1227 | ffi::SQLITE_FCNTL_RESERVE_BYTES => ffi::SQLITE_NOTFOUND,
1228
1229 ffi::SQLITE_FCNTL_SYNC_OMITTED => ffi::SQLITE_NOTFOUND,
1231
1232 ffi::SQLITE_FCNTL_LOCKSTATE => match state.file.current_lock() {
1234 Ok(lock) => {
1235 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1236 *p_arg = lock as i32;
1237 }
1238 ffi::SQLITE_OK
1239 }
1240 Err(err) => state.set_last_error(ffi::SQLITE_ERROR, err),
1241 },
1242
1243 ffi::SQLITE_FCNTL_GET_LOCKPROXYFILE | ffi::SQLITE_FCNTL_SET_LOCKPROXYFILE => {
1245 ffi::SQLITE_NOTFOUND
1246 }
1247
1248 ffi::SQLITE_FCNTL_LAST_ERRNO => {
1250 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1251 *p_arg = state.last_errno;
1252 }
1253 ffi::SQLITE_OK
1254 }
1255
1256 ffi::SQLITE_FCNTL_SIZE_HINT => {
1259 let size_hint = match (p_arg as *mut i64)
1260 .as_ref()
1261 .cloned()
1262 .and_then(|s| u64::try_from(s).ok())
1263 {
1264 Some(chunk_size) => chunk_size,
1265 None => {
1266 return state.set_last_error(
1267 ffi::SQLITE_NOTFOUND,
1268 std::io::Error::new(ErrorKind::Other, "expect size hint arg"),
1269 );
1270 }
1271 };
1272
1273 let current = match state.file.size() {
1277 Ok(size) => size,
1278 Err(err) => return state.set_last_error(ffi::SQLITE_ERROR, err),
1279 };
1280
1281 if current > size_hint {
1282 return ffi::SQLITE_OK;
1283 }
1284
1285 if let Some(chunk_size) = state.chunk_size {
1286 let chunk_size = chunk_size as u64;
1287 let size = ((size_hint + chunk_size - 1) / chunk_size) * chunk_size;
1288 if let Err(err) = state.file.set_len(size) {
1289 return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1290 }
1291 } else if let Err(err) = state.file.set_len(size_hint) {
1292 return state.set_last_error(ffi::SQLITE_IOERR_TRUNCATE, err);
1293 }
1294
1295 ffi::SQLITE_OK
1301 }
1302
1303 ffi::SQLITE_FCNTL_CHUNK_SIZE => {
1307 let chunk_size = match (p_arg as *mut i32)
1308 .as_ref()
1309 .cloned()
1310 .and_then(|s| usize::try_from(s).ok())
1311 {
1312 Some(chunk_size) => chunk_size,
1313 None => {
1314 return state.set_last_error(
1315 ffi::SQLITE_NOTFOUND,
1316 std::io::Error::new(ErrorKind::Other, "expect chunk_size arg"),
1317 );
1318 }
1319 };
1320
1321 if let Err(err) = state.file.set_chunk_size(chunk_size) {
1322 return state.set_last_error(ffi::SQLITE_ERROR, err);
1323 }
1324
1325 state.chunk_size = Some(chunk_size);
1326
1327 ffi::SQLITE_OK
1328 }
1329
1330 ffi::SQLITE_FCNTL_WIN32_AV_RETRY => ffi::SQLITE_NOTFOUND,
1334
1335 ffi::SQLITE_FCNTL_PERSIST_WAL => {
1337 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1338 if *p_arg < 0 {
1339 *p_arg = state.persist_wal as i32;
1341 } else {
1342 state.persist_wal = *p_arg == 1;
1343 }
1344 };
1345
1346 ffi::SQLITE_OK
1347 }
1348
1349 ffi::SQLITE_FCNTL_OVERWRITE => ffi::SQLITE_NOTFOUND,
1352
1353 ffi::SQLITE_FCNTL_VFSNAME => {
1355 if let Some(p_arg) = (p_arg as *mut *const c_char).as_mut() {
1356 let name = ManuallyDrop::new(state.vfs_name.clone());
1357 *p_arg = name.as_ptr();
1358 };
1359
1360 ffi::SQLITE_OK
1361 }
1362
1363 ffi::SQLITE_FCNTL_POWERSAFE_OVERWRITE => {
1365 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1366 if *p_arg < 0 {
1367 *p_arg = state.powersafe_overwrite as i32;
1369 } else {
1370 state.powersafe_overwrite = *p_arg == 1;
1371 }
1372 };
1373
1374 ffi::SQLITE_OK
1375 }
1376
1377 ffi::SQLITE_FCNTL_PRAGMA => ffi::SQLITE_NOTFOUND,
1379
1380 ffi::SQLITE_FCNTL_BUSYHANDLER => ffi::SQLITE_NOTFOUND,
1384
1385 ffi::SQLITE_FCNTL_TEMPFILENAME => {
1387 if let Some(p_arg) = (p_arg as *mut *const c_char).as_mut() {
1388 let name = state.vfs.temporary_name();
1389 let name = CString::new(name.as_bytes()).unwrap();
1391 let name = ManuallyDrop::new(name);
1392 *p_arg = name.as_ptr();
1393 };
1394
1395 ffi::SQLITE_OK
1396 }
1397
1398 ffi::SQLITE_FCNTL_MMAP_SIZE => ffi::SQLITE_NOTFOUND,
1401
1402 ffi::SQLITE_FCNTL_TRACE => {
1405 let trace = CStr::from_ptr(p_arg as *const c_char);
1406 log::trace!("{}", trace.to_string_lossy());
1407 ffi::SQLITE_OK
1408 }
1409
1410 ffi::SQLITE_FCNTL_HAS_MOVED => match state.file.moved() {
1413 Ok(moved) => {
1414 if let Some(p_arg) = (p_arg as *mut i32).as_mut() {
1415 *p_arg = moved as i32;
1416 }
1417 ffi::SQLITE_OK
1418 }
1419 Err(err) => state.set_last_error(ffi::SQLITE_ERROR, err),
1420 },
1421
1422 ffi::SQLITE_FCNTL_SYNC => ffi::SQLITE_OK,
1425
1426 ffi::SQLITE_FCNTL_COMMIT_PHASETWO => ffi::SQLITE_OK,
1429
1430 ffi::SQLITE_FCNTL_WIN32_SET_HANDLE => ffi::SQLITE_NOTFOUND,
1434
1435 ffi::SQLITE_FCNTL_WAL_BLOCK => ffi::SQLITE_NOTFOUND,
1440
1441 ffi::SQLITE_FCNTL_ZIPVFS => ffi::SQLITE_NOTFOUND,
1443
1444 ffi::SQLITE_FCNTL_RBU => ffi::SQLITE_NOTFOUND,
1446
1447 ffi::SQLITE_FCNTL_WIN32_GET_HANDLE => ffi::SQLITE_NOTFOUND,
1450
1451 ffi::SQLITE_FCNTL_PDB => ffi::SQLITE_NOTFOUND,
1453
1454 ffi::SQLITE_FCNTL_BEGIN_ATOMIC_WRITE
1456 | ffi::SQLITE_FCNTL_COMMIT_ATOMIC_WRITE
1457 | ffi::SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE => ffi::SQLITE_NOTFOUND,
1458
1459 ffi::SQLITE_FCNTL_LOCK_TIMEOUT => ffi::SQLITE_NOTFOUND,
1462
1463 ffi::SQLITE_FCNTL_SIZE_LIMIT => ffi::SQLITE_NOTFOUND,
1465
1466 ffi::SQLITE_FCNTL_CKPT_DONE => ffi::SQLITE_OK,
1470
1471 ffi::SQLITE_FCNTL_CKPT_START => ffi::SQLITE_OK,
1474
1475 ffi::SQLITE_FCNTL_EXTERNAL_READER => ffi::SQLITE_NOTFOUND,
1479
1480 ffi::SQLITE_FCNTL_CKSM_FILE => ffi::SQLITE_NOTFOUND,
1482
1483 _ => ffi::SQLITE_NOTFOUND,
1484 }
1485 }
1486
1487 pub unsafe extern "C" fn sector_size<F>(_p_file: *mut ffi::sqlite3_file) -> c_int {
1489 log::trace!("sector_size");
1490
1491 1024
1492 }
1493
1494 pub unsafe extern "C" fn device_characteristics<V, F: DatabaseHandle>(
1496 p_file: *mut ffi::sqlite3_file,
1497 ) -> c_int {
1498 let state = match file_state::<V, F>(p_file) {
1499 Ok(f) => f,
1500 Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1501 };
1502
1503 log::trace!("[{}] device_characteristics", state.id,);
1504
1505 if state.powersafe_overwrite {
1511 ffi::SQLITE_IOCAP_POWERSAFE_OVERWRITE
1512 } else {
1513 0
1514 }
1515 }
1516
1517 pub unsafe extern "C" fn shm_map<V, F: DatabaseHandle>(
1519 p_file: *mut ffi::sqlite3_file,
1520 region_ix: i32,
1521 region_size: i32,
1522 b_extend: i32,
1523 pp: *mut *mut c_void,
1524 ) -> i32 {
1525 let state = match file_state::<V, F>(p_file) {
1526 Ok(f) => f,
1527 Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1528 };
1529 log::trace!(
1530 "[{}] shm_map pg={} sz={} extend={} ({})",
1531 state.id,
1532 region_ix,
1533 region_size,
1534 b_extend,
1535 state.db_name
1536 );
1537
1538 if !F::WalIndex::enabled() {
1539 return ffi::SQLITE_IOERR_SHMLOCK;
1540 }
1541
1542 if region_size != 32768 {
1543 return state.set_last_error(
1544 ffi::SQLITE_IOERR_SHMMAP,
1545 std::io::Error::new(
1546 ErrorKind::Other,
1547 format!(
1548 "encountered region size other than 32kB; got {}",
1549 region_size
1550 ),
1551 ),
1552 );
1553 }
1554
1555 let (wal_index, readonly) = match state.wal_index.as_mut() {
1556 Some((wal_index, readonly)) => (wal_index, *readonly),
1557 None => {
1558 let (wal_index, readonly) = state.wal_index.get_or_insert(
1559 match state
1560 .file
1561 .wal_index(false)
1562 .map(|wal_index| (wal_index, false))
1563 .or_else(|err| {
1564 if err.kind() == ErrorKind::PermissionDenied {
1565 state
1567 .file
1568 .wal_index(true)
1569 .map(|wal_index| (wal_index, true))
1570 .map_err(|_| err)
1571 } else {
1572 Err(err)
1573 }
1574 }) {
1575 Ok((wal_index, readonly)) => (wal_index, readonly),
1576 Err(err) => {
1577 return state.set_last_error(ffi::SQLITE_IOERR_SHMMAP, err);
1578 }
1579 },
1580 );
1581 (wal_index, *readonly)
1582 }
1583 };
1584
1585 let entry = state.wal_index_regions.entry(region_ix as u32);
1586 match entry {
1587 Entry::Occupied(mut entry) => {
1588 *pp = entry.get_mut().as_mut_ptr() as *mut c_void;
1589 }
1590 Entry::Vacant(entry) => {
1591 let mut m = match wal_index.map(region_ix as u32) {
1592 Ok(m) => Box::pin(m),
1593 Err(err) => {
1594 return state.set_last_error(ffi::SQLITE_IOERR_SHMMAP, err);
1595 }
1596 };
1597 *pp = m.as_mut_ptr() as *mut c_void;
1598 entry.insert(m);
1599 }
1600 }
1601
1602 if readonly {
1603 ffi::SQLITE_READONLY
1604 } else {
1605 ffi::SQLITE_OK
1606 }
1607 }
1608
1609 pub unsafe extern "C" fn shm_lock<V, F: DatabaseHandle>(
1611 p_file: *mut ffi::sqlite3_file,
1612 offset: i32,
1613 n: i32,
1614 flags: i32,
1615 ) -> i32 {
1616 let state = match file_state::<V, F>(p_file) {
1617 Ok(f) => f,
1618 Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1619 };
1620 let locking = flags & ffi::SQLITE_SHM_LOCK > 0;
1621 let exclusive = flags & ffi::SQLITE_SHM_EXCLUSIVE > 0;
1622 log::trace!(
1623 "[{}] shm_lock offset={} n={} lock={} exclusive={} (flags={}) ({})",
1624 state.id,
1625 offset,
1626 n,
1627 locking,
1628 exclusive,
1629 flags,
1630 state.db_name
1631 );
1632
1633 let range = offset as u8..(offset + n) as u8;
1634 let lock = match (locking, exclusive) {
1635 (true, true) => wip::WalIndexLock::Exclusive,
1636 (true, false) => wip::WalIndexLock::Shared,
1637 (false, _) => wip::WalIndexLock::None,
1638 };
1639
1640 let (wal_index, readonly) = match state.wal_index.as_mut() {
1641 Some((wal_index, readonly)) => (wal_index, *readonly),
1642 None => {
1643 return state.set_last_error(
1644 ffi::SQLITE_IOERR_SHMLOCK,
1645 std::io::Error::new(
1646 ErrorKind::Other,
1647 "trying to lock wal index, which isn't created yet",
1648 ),
1649 )
1650 }
1651 };
1652
1653 if locking {
1654 let has_exclusive = state
1655 .wal_index_locks
1656 .iter()
1657 .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1658
1659 if !has_exclusive {
1660 log::trace!(
1661 "[{}] does not have wal index write lock, pulling changes",
1662 state.id
1663 );
1664 for (region, data) in &mut state.wal_index_regions {
1665 if let Err(err) = wal_index.pull(*region as u32, data) {
1666 return state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err);
1667 }
1668 }
1669 }
1670 } else {
1671 let releases_any_exclusive = state.wal_index_locks.iter().any(|(region, lock)| {
1672 *lock == wip::WalIndexLock::Exclusive && range.contains(region)
1673 });
1674
1675 if releases_any_exclusive && !readonly {
1677 log::trace!(
1678 "[{}] releasing an exclusive lock, pushing wal index changes",
1679 state.id,
1680 );
1681 for (region, data) in &mut state.wal_index_regions {
1682 if let Err(err) = wal_index.push(*region as u32, data) {
1683 return state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err);
1684 }
1685 }
1686 }
1687 }
1688
1689 match wal_index.lock(range.clone(), lock) {
1690 Ok(true) => {
1691 for region in range {
1692 state.wal_index_locks.insert(region, lock);
1693 }
1694 ffi::SQLITE_OK
1695 }
1696 Ok(false) => ffi::SQLITE_BUSY,
1697 Err(err) => state.set_last_error(ffi::SQLITE_IOERR_SHMLOCK, err),
1698 }
1699 }
1700
1701 pub unsafe extern "C" fn shm_barrier<V, F: DatabaseHandle>(p_file: *mut ffi::sqlite3_file) {
1703 let state = match file_state::<V, F>(p_file) {
1704 Ok(f) => f,
1705 Err(_) => return,
1706 };
1707 log::trace!("[{}] shm_barrier ({})", state.id, state.db_name);
1708
1709 let (wal_index, readonly) = if let Some((wal_index, readonly)) = state.wal_index.as_mut() {
1710 (wal_index, *readonly)
1711 } else {
1712 return;
1713 };
1714
1715 if state.has_exclusive_lock && !readonly {
1716 log::trace!(
1717 "[{}] has exclusive db lock, pushing wal index changes",
1718 state.id,
1719 );
1720 for (region, data) in &mut state.wal_index_regions {
1721 if let Err(err) = wal_index.push(*region as u32, data) {
1722 log::error!("[{}] pushing wal index changes failed: {}", state.id, err)
1723 }
1724 }
1725
1726 return;
1727 }
1728
1729 let has_exclusive = state
1730 .wal_index_locks
1731 .iter()
1732 .any(|(_, lock)| *lock == wip::WalIndexLock::Exclusive);
1733
1734 if !has_exclusive {
1735 log::trace!(
1736 "[{}] does not have wal index write lock, pulling changes",
1737 state.id
1738 );
1739 for (region, data) in &mut state.wal_index_regions {
1740 if let Err(err) = wal_index.pull(*region as u32, data) {
1741 log::error!("[{}] pulling wal index changes failed: {}", state.id, err)
1742 }
1743 }
1744 }
1745 }
1746
1747 pub unsafe extern "C" fn shm_unmap<V, F: DatabaseHandle>(
1749 p_file: *mut ffi::sqlite3_file,
1750 delete_flags: i32,
1751 ) -> i32 {
1752 let state = match file_state::<V, F>(p_file) {
1753 Ok(f) => f,
1754 Err(_) => return ffi::SQLITE_IOERR_SHMMAP,
1755 };
1756 log::trace!(
1757 "[{}] shm_unmap delete={} ({})",
1758 state.id,
1759 delete_flags == 1,
1760 state.db_name
1761 );
1762
1763 state.wal_index_regions.clear();
1764 state.wal_index_locks.clear();
1765
1766 if delete_flags == 1 {
1767 if let Some((wal_index, readonly)) = state.wal_index.take() {
1768 if !readonly {
1769 if let Err(err) = wal_index.delete() {
1770 return state.set_last_error(ffi::SQLITE_ERROR, err);
1771 }
1772 }
1773 }
1774 }
1775
1776 ffi::SQLITE_OK
1777 }
1778}
1779
1780#[cfg(feature = "sqlite_test")]
1802#[inline]
1803unsafe fn simulate_io_error() -> bool {
1804 if (ffi::sqlite3_get_io_error_persist() != 0 && ffi::sqlite3_get_io_error_hit() != 0)
1805 || ffi::sqlite3_dec_io_error_pending() == 1
1806 {
1807 ffi::sqlite3_inc_io_error_hit();
1808 if ffi::sqlite3_get_io_error_benign() == 0 {
1809 ffi::sqlite3_inc_io_error_hardhit();
1810 }
1811
1812 return true;
1813 }
1814
1815 false
1816}
1817
1818#[cfg(feature = "sqlite_test")]
1819#[inline]
1820unsafe fn simulate_diskfull_error() -> bool {
1821 if ffi::sqlite3_get_diskfull_pending() != 0 {
1822 if ffi::sqlite3_get_diskfull_pending() == 1 {
1823 if ffi::sqlite3_get_io_error_benign() == 0 {
1824 ffi::sqlite3_inc_io_error_hardhit();
1825 }
1826 ffi::sqlite3_set_diskfull();
1827 ffi::sqlite3_set_io_error_hit(1);
1828 return true;
1829 } else {
1830 ffi::sqlite3_dec_diskfull_pending();
1831 }
1832 }
1833
1834 false
1835}
1836
1837impl<V> State<V> {
1838 fn set_last_error(&mut self, no: i32, err: std::io::Error) -> i32 {
1839 *(self.last_error.lock().unwrap()) = Some((no, err));
1841 no
1842 }
1843}
1844
1845impl<V, F: DatabaseHandle> FileExt<V, F> {
1846 fn set_last_error(&mut self, no: i32, err: std::io::Error) -> i32 {
1847 *(self.last_error.lock().unwrap()) = Some((no, err));
1849 self.last_errno = no;
1850 no
1851 }
1852}
1853
1854fn null_ptr_error() -> std::io::Error {
1855 std::io::Error::new(ErrorKind::Other, "received null pointer")
1856}
1857
1858unsafe fn vfs_state<'a, V>(ptr: *mut ffi::sqlite3_vfs) -> Result<&'a mut State<V>, std::io::Error> {
1859 let vfs: &mut ffi::sqlite3_vfs = ptr.as_mut().ok_or_else(null_ptr_error)?;
1860 let state = (vfs.pAppData as *mut State<V>)
1861 .as_mut()
1862 .ok_or_else(null_ptr_error)?;
1863 Ok(state)
1864}
1865
1866unsafe fn file_state<'a, V, F: DatabaseHandle>(
1867 ptr: *mut ffi::sqlite3_file,
1868) -> Result<&'a mut FileExt<V, F>, std::io::Error> {
1869 let f = (ptr as *mut FileState<V, F>)
1870 .as_mut()
1871 .ok_or_else(null_ptr_error)?;
1872 let ext = f.ext.assume_init_mut();
1873 Ok(ext)
1874}
1875
1876impl OpenOptions {
1877 fn from_flags(flags: i32) -> Option<Self> {
1878 Some(OpenOptions {
1879 kind: OpenKind::from_flags(flags)?,
1880 access: OpenAccess::from_flags(flags)?,
1881 delete_on_close: flags & ffi::SQLITE_OPEN_DELETEONCLOSE > 0,
1882 })
1883 }
1884
1885 fn to_flags(&self) -> i32 {
1886 self.kind.to_flags()
1887 | self.access.to_flags()
1888 | if self.delete_on_close {
1889 ffi::SQLITE_OPEN_DELETEONCLOSE
1890 } else {
1891 0
1892 }
1893 }
1894}
1895
1896impl OpenKind {
1897 fn from_flags(flags: i32) -> Option<Self> {
1898 match flags {
1899 flags if flags & ffi::SQLITE_OPEN_MAIN_DB > 0 => Some(Self::MainDb),
1900 flags if flags & ffi::SQLITE_OPEN_MAIN_JOURNAL > 0 => Some(Self::MainJournal),
1901 flags if flags & ffi::SQLITE_OPEN_TEMP_DB > 0 => Some(Self::TempDb),
1902 flags if flags & ffi::SQLITE_OPEN_TEMP_JOURNAL > 0 => Some(Self::TempJournal),
1903 flags if flags & ffi::SQLITE_OPEN_TRANSIENT_DB > 0 => Some(Self::TransientDb),
1904 flags if flags & ffi::SQLITE_OPEN_SUBJOURNAL > 0 => Some(Self::SubJournal),
1905 flags if flags & ffi::SQLITE_OPEN_SUPER_JOURNAL > 0 => Some(Self::SuperJournal),
1906 flags if flags & ffi::SQLITE_OPEN_WAL > 0 => Some(Self::Wal),
1907 _ => None,
1908 }
1909 }
1910
1911 fn to_flags(self) -> i32 {
1912 match self {
1913 OpenKind::MainDb => ffi::SQLITE_OPEN_MAIN_DB,
1914 OpenKind::MainJournal => ffi::SQLITE_OPEN_MAIN_JOURNAL,
1915 OpenKind::TempDb => ffi::SQLITE_OPEN_TEMP_DB,
1916 OpenKind::TempJournal => ffi::SQLITE_OPEN_TEMP_JOURNAL,
1917 OpenKind::TransientDb => ffi::SQLITE_OPEN_TRANSIENT_DB,
1918 OpenKind::SubJournal => ffi::SQLITE_OPEN_SUBJOURNAL,
1919 OpenKind::SuperJournal => ffi::SQLITE_OPEN_SUPER_JOURNAL,
1920 OpenKind::Wal => ffi::SQLITE_OPEN_WAL,
1921 }
1922 }
1923}
1924
1925impl OpenAccess {
1926 fn from_flags(flags: i32) -> Option<Self> {
1927 match flags {
1928 flags
1929 if (flags & ffi::SQLITE_OPEN_CREATE > 0)
1930 && (flags & ffi::SQLITE_OPEN_EXCLUSIVE > 0) =>
1931 {
1932 Some(Self::CreateNew)
1933 }
1934 flags if flags & ffi::SQLITE_OPEN_CREATE > 0 => Some(Self::Create),
1935 flags if flags & ffi::SQLITE_OPEN_READWRITE > 0 => Some(Self::Write),
1936 flags if flags & ffi::SQLITE_OPEN_READONLY > 0 => Some(Self::Read),
1937 _ => None,
1938 }
1939 }
1940
1941 fn to_flags(self) -> i32 {
1942 match self {
1943 OpenAccess::Read => ffi::SQLITE_OPEN_READONLY,
1944 OpenAccess::Write => ffi::SQLITE_OPEN_READWRITE,
1945 OpenAccess::Create => ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE,
1946 OpenAccess::CreateNew => {
1947 ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_EXCLUSIVE
1948 }
1949 }
1950 }
1951}
1952
1953impl LockKind {
1954 fn from_i32(lock: i32) -> Option<Self> {
1955 Some(match lock {
1956 ffi::SQLITE_LOCK_NONE => Self::None,
1957 ffi::SQLITE_LOCK_SHARED => Self::Shared,
1958 ffi::SQLITE_LOCK_RESERVED => Self::Reserved,
1959 ffi::SQLITE_LOCK_PENDING => Self::Pending,
1960 ffi::SQLITE_LOCK_EXCLUSIVE => Self::Exclusive,
1961 _ => return None,
1962 })
1963 }
1964
1965 fn to_i32(self) -> i32 {
1966 match self {
1967 Self::None => ffi::SQLITE_LOCK_NONE,
1968 Self::Shared => ffi::SQLITE_LOCK_SHARED,
1969 Self::Reserved => ffi::SQLITE_LOCK_RESERVED,
1970 Self::Pending => ffi::SQLITE_LOCK_PENDING,
1971 Self::Exclusive => ffi::SQLITE_LOCK_EXCLUSIVE,
1972 }
1973 }
1974}
1975
1976impl PartialOrd for LockKind {
1977 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1978 self.to_i32().partial_cmp(&other.to_i32())
1979 }
1980}
1981
1982impl Default for LockKind {
1983 fn default() -> Self {
1984 Self::None
1985 }
1986}
1987
1988#[derive(Default)]
1989pub struct WalDisabled;
1990
1991impl wip::WalIndex for WalDisabled {
1992 fn enabled() -> bool {
1993 false
1994 }
1995
1996 fn map(&mut self, _region: u32) -> Result<[u8; 32768], std::io::Error> {
1997 Err(std::io::Error::new(ErrorKind::Other, "wal is disabled"))
1998 }
1999
2000 fn lock(
2001 &mut self,
2002 _locks: Range<u8>,
2003 _lock: wip::WalIndexLock,
2004 ) -> Result<bool, std::io::Error> {
2005 Err(std::io::Error::new(ErrorKind::Other, "wal is disabled"))
2006 }
2007
2008 fn delete(self) -> Result<(), std::io::Error> {
2009 Ok(())
2010 }
2011}
2012
2013#[derive(Debug)]
2014pub enum RegisterError {
2015 Nul(std::ffi::NulError),
2016 Register(i32),
2017}
2018
2019impl std::error::Error for RegisterError {
2020 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
2021 match self {
2022 Self::Nul(err) => Some(err),
2023 Self::Register(_) => None,
2024 }
2025 }
2026}
2027
2028impl std::fmt::Display for RegisterError {
2029 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2030 match self {
2031 Self::Nul(_) => f.write_str("interior nul byte in name found"),
2032 Self::Register(code) => {
2033 write!(f, "registering sqlite vfs failed with error code: {}", code)
2034 }
2035 }
2036 }
2037}
2038
2039impl From<std::ffi::NulError> for RegisterError {
2040 fn from(err: std::ffi::NulError) -> Self {
2041 Self::Nul(err)
2042 }
2043}
2044
2045#[cfg(test)]
2046mod tests {
2047 use super::*;
2048
2049 #[test]
2050 fn test_lock_order() {
2051 assert!(LockKind::None < LockKind::Shared);
2052 assert!(LockKind::Shared < LockKind::Reserved);
2053 assert!(LockKind::Reserved < LockKind::Pending);
2054 assert!(LockKind::Pending < LockKind::Exclusive);
2055 }
2056}