1use std::ffi::{CStr, CString};
17use std::os::raw::c_char;
18use std::ptr;
19use std::sync::Arc;
20
21use crate::block_store::{DeviceBlockStore, DiskBlockStore, MemoryBlockStore};
22use crate::crypto::ChaChaEngine;
23use crate::error::FsErrorCode;
24use crate::fs::FilesystemCore;
25use crate::model::DEFAULT_BLOCK_SIZE;
26
27pub struct FsHandle {
29 core: FilesystemCore,
30}
31
32#[no_mangle]
45pub unsafe extern "C" fn fs_create(
46 total_blocks: u64,
47 master_key: *const u8,
48 master_key_len: usize,
49) -> *mut FsHandle {
50 if master_key.is_null() || master_key_len == 0 {
51 return ptr::null_mut();
52 }
53 let key_slice = unsafe { std::slice::from_raw_parts(master_key, master_key_len) };
54
55 let store = Arc::new(MemoryBlockStore::new(DEFAULT_BLOCK_SIZE, total_blocks));
56 let crypto = match ChaChaEngine::new(key_slice) {
57 Ok(c) => Arc::new(c),
58 Err(_) => return ptr::null_mut(),
59 };
60
61 let core = FilesystemCore::new(store, crypto);
62 let handle = Box::new(FsHandle { core });
63 Box::into_raw(handle)
64}
65
66#[no_mangle]
82pub unsafe extern "C" fn fs_create_disk(
83 path: *const c_char,
84 total_blocks: u64,
85 block_size: u32,
86 create_new: i32,
87 master_key: *const u8,
88 master_key_len: usize,
89) -> *mut FsHandle {
90 if master_key.is_null() || master_key_len == 0 {
91 return ptr::null_mut();
92 }
93 let path_str = match unsafe { unsafe_cstr_to_str(path) } {
94 Some(s) => s,
95 None => return ptr::null_mut(),
96 };
97 let key_slice = unsafe { std::slice::from_raw_parts(master_key, master_key_len) };
98
99 let bs = if block_size == 0 {
100 DEFAULT_BLOCK_SIZE
101 } else {
102 block_size as usize
103 };
104
105 let store = if create_new != 0 {
106 match DiskBlockStore::create(path_str, bs, total_blocks) {
107 Ok(s) => Arc::new(s),
108 Err(_) => return ptr::null_mut(),
109 }
110 } else {
111 match DiskBlockStore::open(path_str, bs, total_blocks) {
112 Ok(s) => Arc::new(s),
113 Err(_) => return ptr::null_mut(),
114 }
115 };
116
117 let crypto = match ChaChaEngine::new(key_slice) {
118 Ok(c) => Arc::new(c),
119 Err(_) => return ptr::null_mut(),
120 };
121
122 let core = FilesystemCore::new(store, crypto);
123 let handle = Box::new(FsHandle { core });
124 Box::into_raw(handle)
125}
126
127#[no_mangle]
143pub unsafe extern "C" fn fs_create_device(
144 path: *const c_char,
145 total_blocks: u64,
146 block_size: u32,
147 initialize: i32,
148 master_key: *const u8,
149 master_key_len: usize,
150) -> *mut FsHandle {
151 if master_key.is_null() || master_key_len == 0 {
152 return ptr::null_mut();
153 }
154 let path_str = match unsafe { unsafe_cstr_to_str(path) } {
155 Some(s) => s,
156 None => return ptr::null_mut(),
157 };
158 let key_slice = unsafe { std::slice::from_raw_parts(master_key, master_key_len) };
159
160 let bs = if block_size == 0 {
161 DEFAULT_BLOCK_SIZE
162 } else {
163 block_size as usize
164 };
165
166 let store = if initialize != 0 {
167 match DeviceBlockStore::initialize(path_str, bs, total_blocks) {
168 Ok(s) => Arc::new(s),
169 Err(_) => return ptr::null_mut(),
170 }
171 } else {
172 match DeviceBlockStore::open(path_str, bs, total_blocks) {
173 Ok(s) => Arc::new(s),
174 Err(_) => return ptr::null_mut(),
175 }
176 };
177
178 let crypto = match ChaChaEngine::new(key_slice) {
179 Ok(c) => Arc::new(c),
180 Err(_) => return ptr::null_mut(),
181 };
182
183 let core = FilesystemCore::new(store, crypto);
184 let handle = Box::new(FsHandle { core });
185 Box::into_raw(handle)
186}
187
188#[no_mangle]
194pub unsafe extern "C" fn fs_destroy(handle: *mut FsHandle) {
195 if !handle.is_null() {
196 unsafe {
197 drop(Box::from_raw(handle));
198 }
199 }
200}
201
202#[no_mangle]
209pub unsafe extern "C" fn fs_init_filesystem(handle: *mut FsHandle) -> i32 {
210 let Some(h) = (unsafe { handle.as_mut() }) else {
211 return FsErrorCode::InvalidArgument as i32;
212 };
213 match h.core.init_filesystem() {
214 Ok(()) => FsErrorCode::Ok as i32,
215 Err(ref e) => FsErrorCode::from(e) as i32,
216 }
217}
218
219#[no_mangle]
224pub unsafe extern "C" fn fs_open(handle: *mut FsHandle) -> i32 {
225 let Some(h) = (unsafe { handle.as_mut() }) else {
226 return FsErrorCode::InvalidArgument as i32;
227 };
228 match h.core.open() {
229 Ok(()) => FsErrorCode::Ok as i32,
230 Err(ref e) => FsErrorCode::from(e) as i32,
231 }
232}
233
234#[no_mangle]
239pub unsafe extern "C" fn fs_create_file(handle: *mut FsHandle, name: *const c_char) -> i32 {
240 let (h, name_str) = match validate_handle_and_name(handle, name) {
241 Ok(v) => v,
242 Err(code) => return code,
243 };
244 match h.core.create_file(name_str) {
245 Ok(()) => FsErrorCode::Ok as i32,
246 Err(ref e) => FsErrorCode::from(e) as i32,
247 }
248}
249
250#[no_mangle]
256pub unsafe extern "C" fn fs_write_file(
257 handle: *mut FsHandle,
258 name: *const c_char,
259 offset: u64,
260 data: *const u8,
261 data_len: usize,
262) -> i32 {
263 let (h, name_str) = match validate_handle_and_name(handle, name) {
264 Ok(v) => v,
265 Err(code) => return code,
266 };
267 if data.is_null() && data_len > 0 {
268 return FsErrorCode::InvalidArgument as i32;
269 }
270 let slice = if data_len > 0 {
271 unsafe { std::slice::from_raw_parts(data, data_len) }
272 } else {
273 &[]
274 };
275 match h.core.write_file(name_str, offset, slice) {
276 Ok(()) => FsErrorCode::Ok as i32,
277 Err(ref e) => FsErrorCode::from(e) as i32,
278 }
279}
280
281#[no_mangle]
291pub unsafe extern "C" fn fs_read_file(
292 handle: *mut FsHandle,
293 name: *const c_char,
294 offset: u64,
295 len: usize,
296 out_buf: *mut u8,
297 out_len: *mut usize,
298) -> i32 {
299 let (h, name_str) = match validate_handle_and_name(handle, name) {
300 Ok(v) => v,
301 Err(code) => return code,
302 };
303 if out_buf.is_null() || out_len.is_null() {
304 return FsErrorCode::InvalidArgument as i32;
305 }
306
307 match h.core.read_file(name_str, offset, len) {
308 Ok(data) => {
309 let buf_capacity = len;
310 if data.len() > buf_capacity {
311 unsafe { *out_len = data.len() };
312 return FsErrorCode::BufferTooSmall as i32;
313 }
314 unsafe {
315 ptr::copy_nonoverlapping(data.as_ptr(), out_buf, data.len());
316 *out_len = data.len();
317 }
318 FsErrorCode::Ok as i32
319 }
320 Err(ref e) => FsErrorCode::from(e) as i32,
321 }
322}
323
324#[no_mangle]
333pub unsafe extern "C" fn fs_list_root(handle: *mut FsHandle, out_error: *mut i32) -> *mut c_char {
334 let Some(h) = (unsafe { handle.as_mut() }) else {
335 if !out_error.is_null() {
336 unsafe { *out_error = FsErrorCode::InvalidArgument as i32 };
337 }
338 return ptr::null_mut();
339 };
340
341 match h.core.list_directory() {
342 Ok(entries) => {
343 let json = match serde_json::to_string(&entries) {
344 Ok(j) => j,
345 Err(_) => {
346 if !out_error.is_null() {
347 unsafe { *out_error = FsErrorCode::InternalError as i32 };
348 }
349 return ptr::null_mut();
350 }
351 };
352 if !out_error.is_null() {
353 unsafe { *out_error = FsErrorCode::Ok as i32 };
354 }
355 match CString::new(json) {
356 Ok(cs) => cs.into_raw(),
357 Err(_) => {
358 if !out_error.is_null() {
359 unsafe { *out_error = FsErrorCode::InternalError as i32 };
360 }
361 ptr::null_mut()
362 }
363 }
364 }
365 Err(ref e) => {
366 if !out_error.is_null() {
367 unsafe { *out_error = FsErrorCode::from(e) as i32 };
368 }
369 ptr::null_mut()
370 }
371 }
372}
373
374#[no_mangle]
379pub unsafe extern "C" fn fs_create_dir(handle: *mut FsHandle, name: *const c_char) -> i32 {
380 let (h, name_str) = match validate_handle_and_name(handle, name) {
381 Ok(v) => v,
382 Err(code) => return code,
383 };
384 match h.core.create_directory(name_str) {
385 Ok(()) => FsErrorCode::Ok as i32,
386 Err(ref e) => FsErrorCode::from(e) as i32,
387 }
388}
389
390#[no_mangle]
395pub unsafe extern "C" fn fs_remove_file(handle: *mut FsHandle, name: *const c_char) -> i32 {
396 let (h, name_str) = match validate_handle_and_name(handle, name) {
397 Ok(v) => v,
398 Err(code) => return code,
399 };
400 match h.core.remove_file(name_str) {
401 Ok(()) => FsErrorCode::Ok as i32,
402 Err(ref e) => FsErrorCode::from(e) as i32,
403 }
404}
405
406#[no_mangle]
411pub unsafe extern "C" fn fs_rename(
412 handle: *mut FsHandle,
413 old_name: *const c_char,
414 new_name: *const c_char,
415) -> i32 {
416 let Some(h) = (unsafe { handle.as_mut() }) else {
417 return FsErrorCode::InvalidArgument as i32;
418 };
419 let old_str = match unsafe_cstr_to_str(old_name) {
420 Some(s) => s,
421 None => return FsErrorCode::InvalidArgument as i32,
422 };
423 let new_str = match unsafe_cstr_to_str(new_name) {
424 Some(s) => s,
425 None => return FsErrorCode::InvalidArgument as i32,
426 };
427 match h.core.rename(old_str, new_str) {
428 Ok(()) => FsErrorCode::Ok as i32,
429 Err(ref e) => FsErrorCode::from(e) as i32,
430 }
431}
432
433#[no_mangle]
438pub unsafe extern "C" fn fs_sync(handle: *mut FsHandle) -> i32 {
439 let Some(h) = (unsafe { handle.as_mut() }) else {
440 return FsErrorCode::InvalidArgument as i32;
441 };
442 match h.core.sync() {
443 Ok(()) => FsErrorCode::Ok as i32,
444 Err(ref e) => FsErrorCode::from(e) as i32,
445 }
446}
447
448#[no_mangle]
453pub unsafe extern "C" fn fs_free_string(s: *mut c_char) {
454 if !s.is_null() {
455 unsafe {
456 drop(CString::from_raw(s));
457 }
458 }
459}
460
461unsafe fn unsafe_cstr_to_str<'a>(ptr: *const c_char) -> Option<&'a str> {
466 if ptr.is_null() {
467 return None;
468 }
469 unsafe { CStr::from_ptr(ptr) }.to_str().ok()
470}
471
472unsafe fn validate_handle_and_name<'a>(
473 handle: *mut FsHandle,
474 name: *const c_char,
475) -> Result<(&'a mut FsHandle, &'a str), i32> {
476 let h = unsafe { handle.as_mut() }.ok_or(FsErrorCode::InvalidArgument as i32)?;
477 let name_str =
478 unsafe { unsafe_cstr_to_str(name) }.ok_or(FsErrorCode::InvalidArgument as i32)?;
479 Ok((h, name_str))
480}