use super::{Buffer, Completion, File, OpenFlags, IO}; use crate::ext::VfsMod; use crate::io::clock::{Clock, DefaultClock, Instant}; use crate::io::CompletionInner; use crate::{LimboError, Result}; use std::ffi::{c_void, CString}; use std::ptr::NonNull; use std::sync::Arc; use turso_ext::{BufferRef, IOCallback, SendPtr, VfsFileImpl, VfsImpl}; impl Clock for VfsMod { fn now(&self) -> Instant { DefaultClock.now() } } impl IO for VfsMod { fn open_file(&self, path: &str, flags: OpenFlags, direct: bool) -> Result> { let c_path = CString::new(path).map_err(|_| { LimboError::ExtensionError("Failed to convert path to CString".to_string()) })?; let ctx = self.ctx as *mut c_void; let vfs = unsafe { &*self.ctx }; let file = unsafe { (vfs.open)(ctx, c_path.as_ptr(), flags.0, direct) }; if file.is_null() { return Err(LimboError::ExtensionError("File not found".to_string())); } Ok(Arc::new(turso_ext::VfsFileImpl::new(file, self.ctx)?)) } fn remove_file(&self, path: &str) -> Result<()> { let c_path = CString::new(path).map_err(|_| { LimboError::ExtensionError("Failed to convert path to CString".to_string()) })?; let ctx = self.ctx as *mut c_void; let vfs = unsafe { &*self.ctx }; let result = unsafe { (vfs.remove)(ctx, c_path.as_ptr()) }; if !result.is_ok() { return Err(LimboError::ExtensionError(result.to_string())); } Ok(()) } fn step(&self) -> Result<()> { if self.ctx.is_null() { return Err(LimboError::ExtensionError("VFS is null".to_string())); } let vfs = unsafe { &*self.ctx }; let result = unsafe { (vfs.run_once)(vfs.vfs) }; if !result.is_ok() { return Err(LimboError::ExtensionError(result.to_string())); } Ok(()) } fn generate_random_number(&self) -> i64 { if self.ctx.is_null() { return -1; } let vfs = unsafe { &*self.ctx }; unsafe { (vfs.gen_random_number)() } } } impl VfsMod { #[allow(dead_code)] // used in FFI call fn get_current_time(&self) -> String { if self.ctx.is_null() { return "".to_string(); } unsafe { let vfs = &*self.ctx; let chars = (vfs.current_time)(); let cstr = CString::from_raw(chars as *mut _); cstr.to_string_lossy().into_owned() } } } /// # Safety /// the callback wrapper in the extension library is FnOnce, so we know /// that the into_raw/from_raw contract will hold unsafe extern "C" fn callback_fn(result: i32, ctx: SendPtr) { let completion = Completion { inner: (Some(Arc::from_raw(ctx.inner().as_ptr() as *mut CompletionInner))), }; completion.complete(result); } fn to_callback(c: Completion) -> IOCallback { IOCallback::new(callback_fn, unsafe { NonNull::new_unchecked(Arc::into_raw(c.get_inner().clone()) as *mut c_void) }) } impl File for VfsFileImpl { fn lock_file(&self, exclusive: bool) -> Result<()> { let vfs = unsafe { &*self.vfs }; let result = unsafe { (vfs.lock)(self.file, exclusive) }; if result.is_ok() { return Err(LimboError::ExtensionError(result.to_string())); } Ok(()) } fn unlock_file(&self) -> Result<()> { if self.vfs.is_null() { return Err(LimboError::ExtensionError("VFS is null".to_string())); } let vfs = unsafe { &*self.vfs }; let result = unsafe { (vfs.unlock)(self.file) }; if result.is_ok() { return Err(LimboError::ExtensionError(result.to_string())); } Ok(()) } fn pread(&self, pos: u64, c: Completion) -> Result { if self.vfs.is_null() { c.complete(-1); return Err(LimboError::ExtensionError("VFS is null".to_string())); } let r = c.as_read(); let buf = r.buf(); let len = buf.len(); let cb = to_callback(c.clone()); let vfs = unsafe { &*self.vfs }; let res = unsafe { (vfs.read)( self.file, BufferRef::new(buf.as_mut_ptr(), len), pos as i64, cb, ) }; if res.is_error() { return Err(LimboError::ExtensionError("pread failed".to_string())); } Ok(c) } fn pwrite(&self, pos: u64, buffer: Arc, c: Completion) -> Result { if self.vfs.is_null() { c.complete(-1); return Err(LimboError::ExtensionError("VFS is null".to_string())); } let vfs = unsafe { &*self.vfs }; let res = unsafe { let buf = buffer.clone(); let len = buf.len(); let cb = to_callback(c.clone()); (vfs.write)( self.file, BufferRef::new(buf.as_ptr() as *mut u8, len), pos as i64, cb, ) }; if res.is_error() { return Err(LimboError::ExtensionError("pwrite failed".to_string())); } Ok(c) } fn sync(&self, c: Completion) -> Result { if self.vfs.is_null() { c.complete(-1); return Err(LimboError::ExtensionError("VFS is null".to_string())); } let vfs = unsafe { &*self.vfs }; let cb = to_callback(c.clone()); let res = unsafe { (vfs.sync)(self.file, cb) }; if res.is_error() { return Err(LimboError::ExtensionError("sync failed".to_string())); } Ok(c) } fn size(&self) -> Result { let vfs = unsafe { &*self.vfs }; let result = unsafe { (vfs.size)(self.file) }; if result < 0 { Err(LimboError::ExtensionError("size failed".to_string())) } else { Ok(result as u64) } } fn truncate(&self, len: u64, c: Completion) -> Result { if self.vfs.is_null() { c.complete(-1); return Err(LimboError::ExtensionError("VFS is null".to_string())); } let vfs = unsafe { &*self.vfs }; let cb = to_callback(c.clone()); let res = unsafe { (vfs.truncate)(self.file, len as i64, cb) }; if res.is_error() { return Err(LimboError::ExtensionError("truncate failed".to_string())); } Ok(c) } } impl Drop for VfsMod { fn drop(&mut self) { if self.ctx.is_null() { return; } unsafe { let _ = Box::from_raw(self.ctx as *mut VfsImpl); } } }