1#![no_std]
5#![allow(non_snake_case)]
6#![allow(non_camel_case_types)]
7
8extern crate alloc;
9
10#[rustfmt::skip]
12pub mod ffi;
13pub mod memvfs;
14
15use alloc::string::String;
16use alloc::vec::Vec;
17use alloc::{boxed::Box, ffi::CString};
18use alloc::{format, vec};
19use core::time::Duration;
20use core::{cell::RefCell, ffi::CStr, ops::Deref};
21use ffi::*;
22
23#[macro_export]
27macro_rules! bail {
28 ($ex:expr) => {
29 bail!($ex, SQLITE_ERROR);
30 };
31 ($ex:expr, $code: expr) => {
32 if $ex {
33 return $code;
34 }
35 };
36}
37
38#[macro_export]
42macro_rules! check_option {
43 ($ex:expr) => {
44 check_option!($ex, SQLITE_ERROR)
45 };
46 ($ex:expr, $code: expr) => {
47 if let Some(v) = $ex {
48 v
49 } else {
50 return $code;
51 }
52 };
53}
54
55#[macro_export]
59macro_rules! check_result {
60 ($ex:expr) => {
61 check_result!($ex, SQLITE_ERROR)
62 };
63 ($ex:expr, $code: expr) => {
64 if let Ok(v) = $ex {
65 v
66 } else {
67 return $code;
68 }
69 };
70}
71
72#[macro_export]
74macro_rules! unused {
75 ($ex:expr) => {
76 let _ = $ex;
77 };
78}
79
80pub const SQLITE3_HEADER: &str = "SQLite format 3";
82
83pub fn random_name(randomness: fn(&mut [u8])) -> String {
85 const GEN_ASCII_STR_CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
86 abcdefghijklmnopqrstuvwxyz\
87 0123456789";
88 const GEN_LEN: u8 = GEN_ASCII_STR_CHARSET.len() as u8;
89 let mut random_buffer = [0; 32];
90 randomness(&mut random_buffer);
91 random_buffer
92 .into_iter()
93 .map(|e| {
94 let idx = e.saturating_sub(GEN_LEN * (e / GEN_LEN));
95 GEN_ASCII_STR_CHARSET[idx as usize] as char
96 })
97 .collect()
98}
99
100pub struct MemChunksFile {
102 chunks: Vec<Vec<u8>>,
103 chunk_size: Option<usize>,
104 file_size: usize,
105}
106
107impl Default for MemChunksFile {
108 fn default() -> Self {
109 Self::new(512)
110 }
111}
112
113impl MemChunksFile {
114 pub fn new(chunk_size: usize) -> Self {
116 assert!(chunk_size != 0, "chunk size can't be zero");
117 MemChunksFile {
118 chunks: Vec::new(),
119 chunk_size: Some(chunk_size),
120 file_size: 0,
121 }
122 }
123
124 pub fn waiting_for_write() -> Self {
128 MemChunksFile {
129 chunks: Vec::new(),
130 chunk_size: None,
131 file_size: 0,
132 }
133 }
134}
135
136impl VfsFile for MemChunksFile {
137 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<bool> {
138 let Some(chunk_size) = self.chunk_size else {
139 buf.fill(0);
140 return Ok(false);
141 };
142
143 if self.file_size <= offset {
144 buf.fill(0);
145 return Ok(false);
146 }
147
148 if chunk_size == buf.len() && offset % chunk_size == 0 {
149 buf.copy_from_slice(&self.chunks[offset / chunk_size]);
150 Ok(true)
151 } else {
152 let mut size = buf.len();
153 let chunk_idx = offset / chunk_size;
154 let mut remaining_idx = offset % chunk_size;
155 let mut offset = 0;
156
157 for chunk in &self.chunks[chunk_idx..] {
158 let n = core::cmp::min(chunk_size.min(self.file_size) - remaining_idx, size);
159 buf[offset..offset + n].copy_from_slice(&chunk[remaining_idx..remaining_idx + n]);
160 offset += n;
161 size -= n;
162 remaining_idx = 0;
163 if size == 0 {
164 break;
165 }
166 }
167
168 if offset < buf.len() {
169 buf[offset..].fill(0);
170 Ok(false)
171 } else {
172 Ok(true)
173 }
174 }
175 }
176
177 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()> {
178 if buf.is_empty() {
179 return Ok(());
180 }
181
182 let chunk_size = if let Some(chunk_size) = self.chunk_size {
183 chunk_size
184 } else {
185 let size = buf.len();
186 self.chunk_size = Some(size);
187 size
188 };
189
190 let new_length = self.file_size.max(offset + buf.len());
191
192 if chunk_size == buf.len() && offset % chunk_size == 0 {
193 for _ in self.chunks.len()..offset / chunk_size {
194 self.chunks.push(vec![0; chunk_size]);
195 }
196 if let Some(buffer) = self.chunks.get_mut(offset / chunk_size) {
197 buffer.copy_from_slice(buf);
198 } else {
199 self.chunks.push(buf.to_vec());
200 }
201 } else {
202 let mut size = buf.len();
203 let chunk_start_idx = offset / chunk_size;
204 let chunk_end_idx = (offset + size - 1) / chunk_size;
205 let chunks_length = self.chunks.len();
206
207 for _ in chunks_length..=chunk_end_idx {
208 self.chunks.push(vec![0; chunk_size]);
209 }
210
211 let mut remaining_idx = offset % chunk_size;
212 let mut offset = 0;
213
214 for idx in chunk_start_idx..=chunk_end_idx {
215 let n = core::cmp::min(chunk_size - remaining_idx, size);
216 self.chunks[idx][remaining_idx..remaining_idx + n]
217 .copy_from_slice(&buf[offset..offset + n]);
218 offset += n;
219 size -= n;
220 remaining_idx = 0;
221 if size == 0 {
222 break;
223 }
224 }
225 }
226
227 self.file_size = new_length;
228
229 Ok(())
230 }
231
232 fn truncate(&mut self, size: usize) -> VfsResult<()> {
233 if let Some(chunk_size) = self.chunk_size {
234 if size == 0 {
235 core::mem::take(&mut self.chunks);
236 } else {
237 let idx = ((size - 1) / chunk_size) + 1;
238 self.chunks.drain(idx..);
239 }
240 } else if size != 0 {
241 assert_eq!(self.file_size, 0);
242 return Err(VfsError::new(SQLITE_IOERR, "Failed to truncate".into()));
243 }
244 self.file_size = size;
245 Ok(())
246 }
247
248 fn flush(&mut self) -> VfsResult<()> {
249 Ok(())
250 }
251
252 fn size(&self) -> VfsResult<usize> {
253 Ok(self.file_size)
254 }
255}
256
257#[repr(C)]
261pub struct SQLiteVfsFile {
262 pub io_methods: sqlite3_file,
265 pub vfs: *mut sqlite3_vfs,
267 pub flags: i32,
269 pub name_ptr: *const u8,
271 pub name_length: usize,
273}
274
275impl SQLiteVfsFile {
276 pub unsafe fn from_file(file: *mut sqlite3_file) -> &'static SQLiteVfsFile {
282 &*file.cast::<Self>()
283 }
284
285 pub unsafe fn name(&self) -> &'static mut str {
293 core::str::from_utf8_unchecked_mut(core::slice::from_raw_parts_mut(
295 self.name_ptr.cast_mut(),
296 self.name_length,
297 ))
298 }
299
300 pub fn sqlite3_file(&'static self) -> *mut sqlite3_file {
302 self as *const SQLiteVfsFile as *mut sqlite3_file
303 }
304}
305
306#[derive(thiserror::Error, Debug)]
308pub enum RegisterVfsError {
309 #[error("An error occurred converting the given vfs name to a CStr")]
310 ToCStr,
311 #[error("An error occurred while registering vfs with sqlite")]
312 RegisterVfs,
313}
314
315pub fn registered_vfs(vfs_name: &str) -> Result<Option<*mut sqlite3_vfs>, RegisterVfsError> {
317 let name = CString::new(vfs_name).map_err(|_| RegisterVfsError::ToCStr)?;
318 let vfs = unsafe { sqlite3_vfs_find(name.as_ptr()) };
319 Ok((!vfs.is_null()).then_some(vfs))
320}
321
322pub fn register_vfs<IO: SQLiteIoMethods, V: SQLiteVfs<IO>>(
324 vfs_name: &str,
325 app_data: IO::AppData,
326 default_vfs: bool,
327) -> Result<*mut sqlite3_vfs, RegisterVfsError> {
328 let name = CString::new(vfs_name).map_err(|_| RegisterVfsError::ToCStr)?;
329 let name_ptr = name.into_raw();
330
331 let app_data = VfsAppData::new(app_data).leak();
333 let vfs = Box::leak(Box::new(V::vfs(name_ptr, app_data.cast())));
334 let ret = unsafe { sqlite3_vfs_register(vfs, i32::from(default_vfs)) };
335
336 if ret != SQLITE_OK {
337 unsafe {
338 drop(Box::from_raw(vfs));
339 drop(CString::from_raw(name_ptr));
340 drop(VfsAppData::from_raw(app_data));
341 }
342 return Err(RegisterVfsError::RegisterVfs);
343 }
344
345 Ok(vfs as *mut sqlite3_vfs)
346}
347
348#[derive(Debug)]
350pub struct VfsError {
351 code: i32,
352 message: String,
353}
354
355impl VfsError {
356 pub fn new(code: i32, message: String) -> Self {
357 VfsError { code, message }
358 }
359}
360
361pub type VfsResult<T> = Result<T, VfsError>;
363
364pub struct VfsAppData<T> {
367 data: T,
368 last_err: RefCell<Option<(i32, String)>>,
369}
370
371impl<T> VfsAppData<T> {
372 pub fn new(t: T) -> Self {
373 VfsAppData {
374 data: t,
375 last_err: RefCell::new(None),
376 }
377 }
378
379 pub fn leak(self) -> *mut Self {
381 Box::into_raw(Box::new(self))
382 }
383
384 pub unsafe fn from_raw(t: *mut Self) -> VfsAppData<T> {
388 *Box::from_raw(t)
389 }
390
391 pub fn pop_err(&self) -> Option<(i32, String)> {
393 self.last_err.borrow_mut().take()
394 }
395
396 pub fn store_err(&self, err: VfsError) -> i32 {
398 let VfsError { code, message } = err;
399 self.last_err.borrow_mut().replace((code, message));
400 code
401 }
402}
403
404impl<T> Deref for VfsAppData<T> {
406 type Target = T;
407
408 fn deref(&self) -> &Self::Target {
409 &self.data
410 }
411}
412
413pub trait VfsFile {
415 fn read(&self, buf: &mut [u8], offset: usize) -> VfsResult<bool>;
417 fn write(&mut self, buf: &[u8], offset: usize) -> VfsResult<()>;
419 fn truncate(&mut self, size: usize) -> VfsResult<()>;
421 fn flush(&mut self) -> VfsResult<()>;
423 fn size(&self) -> VfsResult<usize>;
425}
426
427pub trait VfsStore<File, AppData> {
429 unsafe fn app_data(vfs: *mut sqlite3_vfs) -> &'static VfsAppData<AppData> {
435 &*(*vfs).pAppData.cast()
436 }
437 fn add_file(vfs: *mut sqlite3_vfs, file: &str, flags: i32) -> VfsResult<()>;
439 fn contains_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<bool>;
441 fn delete_file(vfs: *mut sqlite3_vfs, file: &str) -> VfsResult<()>;
443 fn with_file<F: Fn(&File) -> VfsResult<i32>>(vfs_file: &SQLiteVfsFile, f: F) -> VfsResult<i32>;
445 fn with_file_mut<F: Fn(&mut File) -> VfsResult<i32>>(
447 vfs_file: &SQLiteVfsFile,
448 f: F,
449 ) -> VfsResult<i32>;
450}
451
452pub trait OsCallback {
454 fn sleep(dur: Duration);
455 fn random(buf: &mut [u8]);
456 fn epoch_timestamp_in_ms() -> i64;
457}
458
459#[allow(clippy::missing_safety_doc)]
461pub trait SQLiteVfs<IO: SQLiteIoMethods> {
462 const VERSION: ::core::ffi::c_int;
463 const MAX_PATH_SIZE: ::core::ffi::c_int = 1024;
464
465 fn sleep(dur: Duration);
466 fn random(buf: &mut [u8]);
467 fn epoch_timestamp_in_ms() -> i64;
468
469 fn vfs(
470 vfs_name: *const ::core::ffi::c_char,
471 app_data: *mut VfsAppData<IO::AppData>,
472 ) -> sqlite3_vfs {
473 sqlite3_vfs {
474 iVersion: Self::VERSION,
475 szOsFile: core::mem::size_of::<SQLiteVfsFile>() as i32,
476 mxPathname: Self::MAX_PATH_SIZE,
477 pNext: core::ptr::null_mut(),
478 zName: vfs_name,
479 pAppData: app_data.cast(),
480 xOpen: Some(Self::xOpen),
481 xDelete: Some(Self::xDelete),
482 xAccess: Some(Self::xAccess),
483 xFullPathname: Some(Self::xFullPathname),
484 xDlOpen: None,
485 xDlError: None,
486 xDlSym: None,
487 xDlClose: None,
488 xRandomness: Some(Self::xRandomness),
489 xSleep: Some(Self::xSleep),
490 xCurrentTime: Some(Self::xCurrentTime),
491 xGetLastError: Some(Self::xGetLastError),
492 xCurrentTimeInt64: Some(Self::xCurrentTimeInt64),
493 xSetSystemCall: None,
494 xGetSystemCall: None,
495 xNextSystemCall: None,
496 }
497 }
498
499 unsafe extern "C" fn xOpen(
500 pVfs: *mut sqlite3_vfs,
501 zName: sqlite3_filename,
502 pFile: *mut sqlite3_file,
503 flags: ::core::ffi::c_int,
504 pOutFlags: *mut ::core::ffi::c_int,
505 ) -> ::core::ffi::c_int {
506 Self::xOpenImpl(pVfs, zName, pFile, flags, pOutFlags)
507 }
508
509 unsafe extern "C" fn xOpenImpl(
510 pVfs: *mut sqlite3_vfs,
511 zName: sqlite3_filename,
512 pFile: *mut sqlite3_file,
513 flags: ::core::ffi::c_int,
514 pOutFlags: *mut ::core::ffi::c_int,
515 ) -> ::core::ffi::c_int {
516 let app_data = IO::Store::app_data(pVfs);
517
518 let name = if zName.is_null() {
519 random_name(Self::random)
520 } else {
521 check_result!(CStr::from_ptr(zName).to_str()).into()
522 };
523
524 let exist = match IO::Store::contains_file(pVfs, &name) {
525 Ok(exist) => exist,
526 Err(err) => return app_data.store_err(err),
527 };
528
529 if !exist {
530 if flags & SQLITE_OPEN_CREATE == 0 {
531 return app_data.store_err(VfsError::new(
532 SQLITE_CANTOPEN,
533 format!("file not found: {name}"),
534 ));
535 }
536 if let Err(err) = IO::Store::add_file(pVfs, &name, flags) {
537 return app_data.store_err(err);
538 }
539 }
540
541 let leak = name.leak();
542 let vfs_file = pFile.cast::<SQLiteVfsFile>();
543 (*vfs_file).vfs = pVfs;
544 (*vfs_file).flags = flags;
545 (*vfs_file).name_ptr = leak.as_ptr();
546 (*vfs_file).name_length = leak.len();
547
548 (*pFile).pMethods = &IO::METHODS;
549
550 if !pOutFlags.is_null() {
551 *pOutFlags = flags;
552 }
553
554 SQLITE_OK
555 }
556
557 unsafe extern "C" fn xDelete(
558 pVfs: *mut sqlite3_vfs,
559 zName: *const ::core::ffi::c_char,
560 syncDir: ::core::ffi::c_int,
561 ) -> ::core::ffi::c_int {
562 unused!(syncDir);
563
564 let app_data = IO::Store::app_data(pVfs);
565 bail!(zName.is_null(), SQLITE_IOERR_DELETE);
566 let s = check_result!(CStr::from_ptr(zName).to_str());
567 if let Err(err) = IO::Store::delete_file(pVfs, s) {
568 app_data.store_err(err)
569 } else {
570 SQLITE_OK
571 }
572 }
573
574 unsafe extern "C" fn xAccess(
575 pVfs: *mut sqlite3_vfs,
576 zName: *const ::core::ffi::c_char,
577 flags: ::core::ffi::c_int,
578 pResOut: *mut ::core::ffi::c_int,
579 ) -> ::core::ffi::c_int {
580 unused!(flags);
581
582 *pResOut = if zName.is_null() {
583 0
584 } else {
585 let app_data = IO::Store::app_data(pVfs);
586 let file = check_result!(CStr::from_ptr(zName).to_str());
587 let exist = match IO::Store::contains_file(pVfs, file) {
588 Ok(exist) => exist,
589 Err(err) => return app_data.store_err(err),
590 };
591 i32::from(exist)
592 };
593
594 SQLITE_OK
595 }
596
597 unsafe extern "C" fn xFullPathname(
598 pVfs: *mut sqlite3_vfs,
599 zName: *const ::core::ffi::c_char,
600 nOut: ::core::ffi::c_int,
601 zOut: *mut ::core::ffi::c_char,
602 ) -> ::core::ffi::c_int {
603 unused!(pVfs);
604 bail!(zName.is_null() || zOut.is_null(), SQLITE_CANTOPEN);
605 let len = CStr::from_ptr(zName).to_bytes_with_nul().len();
606 bail!(len > nOut as usize, SQLITE_CANTOPEN);
607 zName.copy_to(zOut, len);
608 SQLITE_OK
609 }
610
611 unsafe extern "C" fn xGetLastError(
612 pVfs: *mut sqlite3_vfs,
613 nOut: ::core::ffi::c_int,
614 zOut: *mut ::core::ffi::c_char,
615 ) -> ::core::ffi::c_int {
616 let app_data = IO::Store::app_data(pVfs);
617 let Some((code, msg)) = app_data.pop_err() else {
618 return SQLITE_OK;
619 };
620 if !zOut.is_null() {
621 let nOut = nOut as usize;
622 let count = msg.len().min(nOut);
623 msg.as_ptr().copy_to(zOut.cast(), count);
624 let zero = match nOut.cmp(&msg.len()) {
625 core::cmp::Ordering::Less | core::cmp::Ordering::Equal => nOut,
626 core::cmp::Ordering::Greater => msg.len() + 1,
627 };
628 if zero > 0 {
629 core::ptr::write(zOut.add(zero - 1), 0);
630 }
631 }
632 code
633 }
634
635 unsafe extern "C" fn xRandomness(
637 pVfs: *mut sqlite3_vfs,
638 nByte: ::core::ffi::c_int,
639 zOut: *mut ::core::ffi::c_char,
640 ) -> ::core::ffi::c_int {
641 unused!(pVfs);
642 let slice = core::slice::from_raw_parts_mut(zOut.cast(), nByte as usize);
643 Self::random(slice);
644 nByte
645 }
646
647 unsafe extern "C" fn xCurrentTime(
649 pVfs: *mut sqlite3_vfs,
650 pTimeOut: *mut f64,
651 ) -> ::core::ffi::c_int {
652 unused!(pVfs);
653 *pTimeOut = 2440587.5 + (Self::epoch_timestamp_in_ms() as f64 / 86400000.0);
654 SQLITE_OK
655 }
656
657 unsafe extern "C" fn xCurrentTimeInt64(
659 pVfs: *mut sqlite3_vfs,
660 pOut: *mut sqlite3_int64,
661 ) -> ::core::ffi::c_int {
662 unused!(pVfs);
663 *pOut = ((2440587.5 * 86400000.0) + Self::epoch_timestamp_in_ms() as f64) as sqlite3_int64;
664 SQLITE_OK
665 }
666
667 unsafe extern "C" fn xSleep(
668 pVfs: *mut sqlite3_vfs,
669 microseconds: ::core::ffi::c_int,
670 ) -> ::core::ffi::c_int {
671 unused!(pVfs);
672 let dur = Duration::from_micros(microseconds as u64);
673 Self::sleep(dur);
674 SQLITE_OK
675 }
676}
677
678#[allow(clippy::missing_safety_doc)]
680pub trait SQLiteIoMethods {
681 type File: VfsFile;
682 type AppData: 'static;
683 type Store: VfsStore<Self::File, Self::AppData>;
684
685 const VERSION: ::core::ffi::c_int;
686
687 const METHODS: sqlite3_io_methods = sqlite3_io_methods {
688 iVersion: Self::VERSION,
689 xClose: Some(Self::xClose),
690 xRead: Some(Self::xRead),
691 xWrite: Some(Self::xWrite),
692 xTruncate: Some(Self::xTruncate),
693 xSync: Some(Self::xSync),
694 xFileSize: Some(Self::xFileSize),
695 xLock: Some(Self::xLock),
696 xUnlock: Some(Self::xUnlock),
697 xCheckReservedLock: Some(Self::xCheckReservedLock),
698 xFileControl: Some(Self::xFileControl),
699 xSectorSize: Some(Self::xSectorSize),
700 xDeviceCharacteristics: Some(Self::xDeviceCharacteristics),
701 xShmMap: None,
702 xShmLock: None,
703 xShmBarrier: None,
704 xShmUnmap: None,
705 xFetch: None,
706 xUnfetch: None,
707 };
708
709 unsafe extern "C" fn xClose(pFile: *mut sqlite3_file) -> ::core::ffi::c_int {
710 Self::xCloseImpl(pFile)
711 }
712
713 unsafe extern "C" fn xCloseImpl(pFile: *mut sqlite3_file) -> ::core::ffi::c_int {
714 let vfs_file = SQLiteVfsFile::from_file(pFile);
715 let app_data = Self::Store::app_data(vfs_file.vfs);
716
717 if vfs_file.flags & SQLITE_OPEN_DELETEONCLOSE != 0 {
718 if let Err(err) = Self::Store::delete_file(vfs_file.vfs, vfs_file.name()) {
719 return app_data.store_err(err);
720 }
721 }
722
723 drop(Box::from_raw(vfs_file.name()));
724
725 SQLITE_OK
726 }
727
728 unsafe extern "C" fn xRead(
729 pFile: *mut sqlite3_file,
730 zBuf: *mut ::core::ffi::c_void,
731 iAmt: ::core::ffi::c_int,
732 iOfst: sqlite3_int64,
733 ) -> ::core::ffi::c_int {
734 let vfs_file = SQLiteVfsFile::from_file(pFile);
735 let app_data = Self::Store::app_data(vfs_file.vfs);
736
737 let f = |file: &Self::File| {
738 let size = iAmt as usize;
739 let offset = iOfst as usize;
740 let slice = core::slice::from_raw_parts_mut(zBuf.cast::<u8>(), size);
741 let code = if file.read(slice, offset)? {
742 SQLITE_OK
743 } else {
744 SQLITE_IOERR_SHORT_READ
745 };
746 Ok(code)
747 };
748
749 match Self::Store::with_file(vfs_file, f) {
750 Ok(code) => code,
751 Err(err) => app_data.store_err(err),
752 }
753 }
754
755 unsafe extern "C" fn xWrite(
756 pFile: *mut sqlite3_file,
757 zBuf: *const ::core::ffi::c_void,
758 iAmt: ::core::ffi::c_int,
759 iOfst: sqlite3_int64,
760 ) -> ::core::ffi::c_int {
761 let vfs_file = SQLiteVfsFile::from_file(pFile);
762 let app_data = Self::Store::app_data(vfs_file.vfs);
763
764 let f = |file: &mut Self::File| {
765 let (offset, size) = (iOfst as usize, iAmt as usize);
766 let slice = core::slice::from_raw_parts(zBuf.cast::<u8>(), size);
767 file.write(slice, offset)?;
768 Ok(SQLITE_OK)
769 };
770
771 match Self::Store::with_file_mut(vfs_file, f) {
772 Ok(code) => code,
773 Err(err) => app_data.store_err(err),
774 }
775 }
776
777 unsafe extern "C" fn xTruncate(
778 pFile: *mut sqlite3_file,
779 size: sqlite3_int64,
780 ) -> ::core::ffi::c_int {
781 let vfs_file = SQLiteVfsFile::from_file(pFile);
782 let app_data = Self::Store::app_data(vfs_file.vfs);
783
784 let f = |file: &mut Self::File| {
785 file.truncate(size as usize)?;
786 Ok(SQLITE_OK)
787 };
788
789 match Self::Store::with_file_mut(vfs_file, f) {
790 Ok(code) => code,
791 Err(err) => app_data.store_err(err),
792 }
793 }
794
795 unsafe extern "C" fn xSync(
796 pFile: *mut sqlite3_file,
797 flags: ::core::ffi::c_int,
798 ) -> ::core::ffi::c_int {
799 unused!(flags);
800
801 let vfs_file = SQLiteVfsFile::from_file(pFile);
802 let app_data = Self::Store::app_data(vfs_file.vfs);
803
804 let f = |file: &mut Self::File| {
805 file.flush()?;
806 Ok(SQLITE_OK)
807 };
808
809 match Self::Store::with_file_mut(vfs_file, f) {
810 Ok(code) => code,
811 Err(err) => app_data.store_err(err),
812 }
813 }
814
815 unsafe extern "C" fn xFileSize(
816 pFile: *mut sqlite3_file,
817 pSize: *mut sqlite3_int64,
818 ) -> ::core::ffi::c_int {
819 let vfs_file = SQLiteVfsFile::from_file(pFile);
820 let app_data = Self::Store::app_data(vfs_file.vfs);
821
822 let f = |file: &Self::File| {
823 file.size().map(|size| {
824 *pSize = size as sqlite3_int64;
825 })?;
826 Ok(SQLITE_OK)
827 };
828
829 match Self::Store::with_file(vfs_file, f) {
830 Ok(code) => code,
831 Err(err) => app_data.store_err(err),
832 }
833 }
834
835 unsafe extern "C" fn xLock(
836 pFile: *mut sqlite3_file,
837 eLock: ::core::ffi::c_int,
838 ) -> ::core::ffi::c_int {
839 unused!((pFile, eLock));
841 SQLITE_OK
842 }
843
844 unsafe extern "C" fn xUnlock(
845 pFile: *mut sqlite3_file,
846 eLock: ::core::ffi::c_int,
847 ) -> ::core::ffi::c_int {
848 unused!((pFile, eLock));
850 SQLITE_OK
851 }
852
853 unsafe extern "C" fn xCheckReservedLock(
854 pFile: *mut sqlite3_file,
855 pResOut: *mut ::core::ffi::c_int,
856 ) -> ::core::ffi::c_int {
857 unused!(pFile);
858 *pResOut = 0;
859 SQLITE_OK
860 }
861
862 unsafe extern "C" fn xFileControl(
863 pFile: *mut sqlite3_file,
864 op: ::core::ffi::c_int,
865 pArg: *mut ::core::ffi::c_void,
866 ) -> ::core::ffi::c_int {
867 unused!((pFile, op, pArg));
868 SQLITE_NOTFOUND
869 }
870
871 unsafe extern "C" fn xSectorSize(pFile: *mut sqlite3_file) -> ::core::ffi::c_int {
872 unused!(pFile);
873 512
874 }
875
876 unsafe extern "C" fn xDeviceCharacteristics(pFile: *mut sqlite3_file) -> ::core::ffi::c_int {
877 unused!(pFile);
878 0
879 }
880}
881
882#[derive(thiserror::Error, Debug)]
883pub enum ImportDbError {
884 #[error("Byte array size is invalid for an SQLite db.")]
885 InvalidDbSize,
886 #[error("Input does not contain an SQLite database header.")]
887 InvalidHeader,
888 #[error("Page size must be a power of two between 512 and 65536 inclusive")]
889 InvalidPageSize,
890}
891
892pub fn check_import_db(bytes: &[u8]) -> Result<usize, ImportDbError> {
895 let length = bytes.len();
896
897 if length < 512 || length % 512 != 0 {
898 return Err(ImportDbError::InvalidDbSize);
899 }
900
901 if !bytes.starts_with(SQLITE3_HEADER.as_bytes()) {
902 return Err(ImportDbError::InvalidHeader);
903 }
904
905 let page_size = u16::from_be_bytes([bytes[16], bytes[17]]);
908 let page_size = if page_size == 1 {
909 65536
910 } else {
911 usize::from(page_size)
912 };
913
914 Ok(page_size)
915}
916
917pub fn check_db_and_page_size(db_size: usize, page_size: usize) -> Result<(), ImportDbError> {
919 if !(page_size.is_power_of_two() && (512..=65536).contains(&page_size)) {
920 return Err(ImportDbError::InvalidPageSize);
921 }
922 if db_size % page_size != 0 {
923 return Err(ImportDbError::InvalidDbSize);
924 }
925 Ok(())
926}
927
928#[doc(hidden)]
930pub mod test_suite {
931 use alloc::vec;
932
933 use super::{
934 sqlite3_file, sqlite3_vfs, SQLiteVfsFile, VfsAppData, VfsError, VfsFile, VfsResult,
935 VfsStore, SQLITE_IOERR, SQLITE_OK, SQLITE_OPEN_CREATE, SQLITE_OPEN_MAIN_DB,
936 SQLITE_OPEN_READWRITE,
937 };
938
939 fn test_vfs_file<File: VfsFile>(file: &mut File) -> VfsResult<i32> {
940 let base_offset = 1024 * 1024;
941
942 let mut write_buffer = vec![42; 64 * 1024];
943 let mut read_buffer = vec![42; base_offset + write_buffer.len()];
944 let hw = "hello world!";
945 write_buffer[0..hw.len()].copy_from_slice(hw.as_bytes());
946
947 file.write(&write_buffer, 0)?;
948 assert!(!file.read(&mut read_buffer, 0)?);
949 if &read_buffer[0..hw.len()] != hw.as_bytes()
950 || read_buffer[hw.len()..write_buffer.len()]
951 .iter()
952 .any(|&x| x != 42)
953 || read_buffer[write_buffer.len()..].iter().any(|&x| x != 0)
954 {
955 Err(VfsError::new(SQLITE_IOERR, "incorrect buffer data".into()))?;
956 }
957 if file.size()? != write_buffer.len() {
958 Err(VfsError::new(
959 SQLITE_IOERR,
960 "incorrect buffer length".into(),
961 ))?;
962 }
963
964 file.write(&write_buffer, base_offset)?;
965 if file.size()? != base_offset + write_buffer.len() {
966 Err(VfsError::new(
967 SQLITE_IOERR,
968 "incorrect buffer length".into(),
969 ))?;
970 }
971 assert!(file.read(&mut read_buffer, 0)?);
972 if &read_buffer[0..hw.len()] != hw.as_bytes()
973 || read_buffer[hw.len()..write_buffer.len()]
974 .iter()
975 .any(|&x| x != 42)
976 || read_buffer[write_buffer.len()..base_offset]
977 .iter()
978 .all(|&x| x != 0)
979 || &read_buffer[base_offset..base_offset + hw.len()] != hw.as_bytes()
980 || read_buffer[base_offset + hw.len()..]
981 .iter()
982 .any(|&x| x != 42)
983 {
984 Err(VfsError::new(SQLITE_IOERR, "incorrect buffer data".into()))?;
985 }
986
987 Ok(SQLITE_OK)
988 }
989
990 pub fn test_vfs_store<AppData, File: VfsFile, Store: VfsStore<File, AppData>>(
991 vfs_data: VfsAppData<AppData>,
992 ) -> VfsResult<()> {
993 let layout = core::alloc::Layout::new::<sqlite3_vfs>();
994 let vfs = unsafe {
995 let vfs = alloc::alloc::alloc(layout) as *mut sqlite3_vfs;
996 (*vfs).pAppData = vfs_data.leak().cast();
997 vfs
998 };
999
1000 let test_file = |filename: &str, flags: i32| {
1001 if Store::contains_file(vfs, filename)? {
1002 Err(VfsError::new(SQLITE_IOERR, "found file before test".into()))?;
1003 }
1004
1005 let vfs_file = SQLiteVfsFile {
1006 io_methods: sqlite3_file {
1007 pMethods: core::ptr::null(),
1008 },
1009 vfs,
1010 flags,
1011 name_ptr: filename.as_ptr(),
1012 name_length: filename.len(),
1013 };
1014
1015 Store::add_file(vfs, filename, flags)?;
1016
1017 if !Store::contains_file(vfs, filename)? {
1018 Err(VfsError::new(
1019 SQLITE_IOERR,
1020 "not found file after create".into(),
1021 ))?;
1022 }
1023
1024 Store::with_file_mut(&vfs_file, |file| test_vfs_file(file))?;
1025
1026 Store::delete_file(vfs, filename)?;
1027
1028 if Store::contains_file(vfs, filename)? {
1029 Err(VfsError::new(
1030 SQLITE_IOERR,
1031 "found file after delete".into(),
1032 ))?;
1033 }
1034
1035 Ok(())
1036 };
1037
1038 test_file(
1039 "___test_vfs_store#1___",
1040 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB,
1041 )?;
1042
1043 test_file(
1044 "___test_vfs_store#2___",
1045 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
1046 )?;
1047
1048 unsafe {
1049 drop(VfsAppData::<AppData>::from_raw((*vfs).pAppData as *mut _));
1050 alloc::alloc::dealloc(vfs.cast(), layout);
1051 }
1052
1053 Ok(())
1054 }
1055}
1056
1057#[cfg(test)]
1058mod tests {
1059 use crate::random_name;
1060 use crate::{MemChunksFile, VfsFile};
1061
1062 #[test]
1063 fn test_chunks_file() {
1064 let mut file = MemChunksFile::new(512);
1065 file.write(&[], 0).unwrap();
1066 assert!(file.size().unwrap() == 0);
1067
1068 let mut buffer = [1; 2];
1069 let ret = file.read(&mut buffer, 0).unwrap();
1070 assert_eq!(ret, false);
1071 assert_eq!([0; 2], buffer);
1072
1073 file.write(&[1], 0).unwrap();
1074 assert!(file.size().unwrap() == 1);
1075 let mut buffer = [2; 2];
1076 let ret = file.read(&mut buffer, 0).unwrap();
1077 assert_eq!(ret, false);
1078 assert_eq!([1, 0], buffer);
1079
1080 let mut file = MemChunksFile::new(512);
1081 file.write(&[1; 512], 0).unwrap();
1082 assert!(file.size().unwrap() == 512);
1083 assert!(file.chunks.len() == 1);
1084
1085 file.truncate(512).unwrap();
1086 assert!(file.size().unwrap() == 512);
1087 assert!(file.chunks.len() == 1);
1088
1089 file.write(&[41, 42, 43], 511).unwrap();
1090 assert!(file.size().unwrap() == 514);
1091 assert!(file.chunks.len() == 2);
1092
1093 let mut buffer = [0; 3];
1094 let ret = file.read(&mut buffer, 511).unwrap();
1095 assert_eq!(ret, true);
1096 assert_eq!(buffer, [41, 42, 43]);
1097
1098 file.truncate(513).unwrap();
1099 assert!(file.size().unwrap() == 513);
1100 assert!(file.chunks.len() == 2);
1101
1102 file.write(&[1], 2048).unwrap();
1103 assert!(file.size().unwrap() == 2049);
1104 assert!(file.chunks.len() == 5);
1105
1106 file.truncate(0).unwrap();
1107 assert!(file.size().unwrap() == 0);
1108 assert!(file.chunks.len() == 0);
1109 }
1110
1111 #[test]
1112 fn random_name_is_valid() {
1113 fn random(buf: &mut [u8]) {
1114 rand::fill(buf);
1115 }
1116 let name_1 = random_name(random);
1117 let name_2 = random_name(random);
1118 assert!(name_1.is_ascii(), "Expected an ascii-name: `{name_1}`");
1119 assert!(name_2.is_ascii(), "Expected an ascii-name: `{name_2}`");
1120 assert_ne!(name_1, name_2);
1121 }
1122}