Skip to main content

limbo_core/io/
vfs.rs

1use super::{Buffer, Completion, File, MemoryIO, OpenFlags, IO};
2use crate::ext::VfsMod;
3use crate::io::clock::{Clock, Instant};
4use crate::{LimboError, Result};
5use limbo_ext::{VfsFileImpl, VfsImpl};
6use std::cell::RefCell;
7use std::ffi::{c_void, CString};
8use std::sync::Arc;
9
10impl Clock for VfsMod {
11    fn now(&self) -> Instant {
12        let now = chrono::Local::now();
13        Instant {
14            secs: now.timestamp(),
15            micros: now.timestamp_subsec_micros(),
16        }
17    }
18}
19
20impl IO for VfsMod {
21    fn open_file(&self, path: &str, flags: OpenFlags, direct: bool) -> Result<Arc<dyn File>> {
22        let c_path = CString::new(path).map_err(|_| {
23            LimboError::ExtensionError("Failed to convert path to CString".to_string())
24        })?;
25        let ctx = self.ctx as *mut c_void;
26        let vfs = unsafe { &*self.ctx };
27        let file = unsafe { (vfs.open)(ctx, c_path.as_ptr(), flags.0, direct) };
28        if file.is_null() {
29            return Err(LimboError::ExtensionError("File not found".to_string()));
30        }
31        Ok(Arc::new(limbo_ext::VfsFileImpl::new(file, self.ctx)?))
32    }
33
34    fn run_once(&self) -> Result<()> {
35        if self.ctx.is_null() {
36            return Err(LimboError::ExtensionError("VFS is null".to_string()));
37        }
38        let vfs = unsafe { &*self.ctx };
39        let result = unsafe { (vfs.run_once)(vfs.vfs) };
40        if !result.is_ok() {
41            return Err(LimboError::ExtensionError(result.to_string()));
42        }
43        Ok(())
44    }
45
46    fn wait_for_completion(&self, _c: Arc<Completion>) -> Result<()> {
47        todo!();
48    }
49
50    fn generate_random_number(&self) -> i64 {
51        if self.ctx.is_null() {
52            return -1;
53        }
54        let vfs = unsafe { &*self.ctx };
55        unsafe { (vfs.gen_random_number)() }
56    }
57
58    fn get_memory_io(&self) -> Arc<MemoryIO> {
59        Arc::new(MemoryIO::new())
60    }
61}
62
63impl VfsMod {
64    #[allow(dead_code)] // used in FFI call
65    fn get_current_time(&self) -> String {
66        if self.ctx.is_null() {
67            return "".to_string();
68        }
69        unsafe {
70            let vfs = &*self.ctx;
71            let chars = (vfs.current_time)();
72            let cstr = CString::from_raw(chars as *mut _);
73            cstr.to_string_lossy().into_owned()
74        }
75    }
76}
77
78impl File for VfsFileImpl {
79    fn lock_file(&self, exclusive: bool) -> Result<()> {
80        let vfs = unsafe { &*self.vfs };
81        let result = unsafe { (vfs.lock)(self.file, exclusive) };
82        if result.is_ok() {
83            return Err(LimboError::ExtensionError(result.to_string()));
84        }
85        Ok(())
86    }
87
88    fn unlock_file(&self) -> Result<()> {
89        if self.vfs.is_null() {
90            return Err(LimboError::ExtensionError("VFS is null".to_string()));
91        }
92        let vfs = unsafe { &*self.vfs };
93        let result = unsafe { (vfs.unlock)(self.file) };
94        if result.is_ok() {
95            return Err(LimboError::ExtensionError(result.to_string()));
96        }
97        Ok(())
98    }
99
100    fn pread(&self, pos: usize, c: Arc<Completion>) -> Result<()> {
101        let r = match &*c {
102            Completion::Read(ref r) => r,
103            _ => unreachable!(),
104        };
105        let result = {
106            let mut buf = r.buf_mut();
107            let count = buf.len();
108            let vfs = unsafe { &*self.vfs };
109            unsafe { (vfs.read)(self.file, buf.as_mut_ptr(), count, pos as i64) }
110        };
111        if result < 0 {
112            Err(LimboError::ExtensionError("pread failed".to_string()))
113        } else {
114            c.complete(result);
115            Ok(())
116        }
117    }
118
119    fn pwrite(&self, pos: usize, buffer: Arc<RefCell<Buffer>>, c: Arc<Completion>) -> Result<()> {
120        let buf = buffer.borrow();
121        let count = buf.as_slice().len();
122        if self.vfs.is_null() {
123            return Err(LimboError::ExtensionError("VFS is null".to_string()));
124        }
125        let vfs = unsafe { &*self.vfs };
126        let result = unsafe {
127            (vfs.write)(
128                self.file,
129                buf.as_slice().as_ptr() as *mut u8,
130                count,
131                pos as i64,
132            )
133        };
134
135        if result < 0 {
136            Err(LimboError::ExtensionError("pwrite failed".to_string()))
137        } else {
138            c.complete(result);
139            Ok(())
140        }
141    }
142
143    fn sync(&self, c: Arc<Completion>) -> Result<()> {
144        let vfs = unsafe { &*self.vfs };
145        let result = unsafe { (vfs.sync)(self.file) };
146        if result < 0 {
147            Err(LimboError::ExtensionError("sync failed".to_string()))
148        } else {
149            c.complete(0);
150            Ok(())
151        }
152    }
153
154    fn size(&self) -> Result<u64> {
155        let vfs = unsafe { &*self.vfs };
156        let result = unsafe { (vfs.size)(self.file) };
157        if result < 0 {
158            Err(LimboError::ExtensionError("size failed".to_string()))
159        } else {
160            Ok(result as u64)
161        }
162    }
163
164    fn truncate(&self, _len: usize, c: Arc<Completion>) -> Result<()> {
165        // External VFS plugins do not expose a truncate hook in the current ABI;
166        // completing without shrinking is safe because the WAL header rewrite
167        // already invalidates leftover frames via salt mismatch.
168        c.complete(0);
169        Ok(())
170    }
171}
172
173impl Drop for VfsMod {
174    fn drop(&mut self) {
175        if self.ctx.is_null() {
176            return;
177        }
178        unsafe {
179            let _ = Box::from_raw(self.ctx as *mut VfsImpl);
180        }
181    }
182}