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::cached_store::CachedBlockStore;
23use crate::crypto::ChaChaEngine;
24use crate::error::FsErrorCode;
25use crate::fs::FilesystemCore;
26use crate::model::DEFAULT_BLOCK_SIZE;
27use crate::network_store::NetworkBlockStoreConfig;
28
29pub struct FsHandle {
31 core: FilesystemCore,
32}
33
34#[no_mangle]
47pub unsafe extern "C" fn fs_create(
48 total_blocks: u64,
49 master_key: *const u8,
50 master_key_len: usize,
51) -> *mut FsHandle {
52 if master_key.is_null() || master_key_len == 0 {
53 return ptr::null_mut();
54 }
55 let key_slice = unsafe { std::slice::from_raw_parts(master_key, master_key_len) };
56
57 let store = Arc::new(MemoryBlockStore::new(DEFAULT_BLOCK_SIZE, total_blocks));
58 let crypto = match ChaChaEngine::new(key_slice) {
59 Ok(c) => Arc::new(c),
60 Err(_) => return ptr::null_mut(),
61 };
62
63 let core = FilesystemCore::new(store, crypto);
64 let handle = Box::new(FsHandle { core });
65 Box::into_raw(handle)
66}
67
68#[no_mangle]
84pub unsafe extern "C" fn fs_create_disk(
85 path: *const c_char,
86 total_blocks: u64,
87 block_size: u32,
88 create_new: i32,
89 master_key: *const u8,
90 master_key_len: usize,
91) -> *mut FsHandle {
92 if master_key.is_null() || master_key_len == 0 {
93 return ptr::null_mut();
94 }
95 let path_str = match unsafe { unsafe_cstr_to_str(path) } {
96 Some(s) => s,
97 None => return ptr::null_mut(),
98 };
99 let key_slice = unsafe { std::slice::from_raw_parts(master_key, master_key_len) };
100
101 let bs = if block_size == 0 {
102 DEFAULT_BLOCK_SIZE
103 } else {
104 block_size as usize
105 };
106
107 let store = if create_new != 0 {
108 match DiskBlockStore::create(path_str, bs, total_blocks) {
109 Ok(s) => Arc::new(s),
110 Err(_) => return ptr::null_mut(),
111 }
112 } else {
113 match DiskBlockStore::open(path_str, bs, total_blocks) {
114 Ok(s) => Arc::new(s),
115 Err(_) => return ptr::null_mut(),
116 }
117 };
118
119 let crypto = match ChaChaEngine::new(key_slice) {
120 Ok(c) => Arc::new(c),
121 Err(_) => return ptr::null_mut(),
122 };
123
124 let core = FilesystemCore::new(store, crypto);
125 let handle = Box::new(FsHandle { core });
126 Box::into_raw(handle)
127}
128
129#[no_mangle]
145pub unsafe extern "C" fn fs_create_device(
146 path: *const c_char,
147 total_blocks: u64,
148 block_size: u32,
149 initialize: i32,
150 master_key: *const u8,
151 master_key_len: usize,
152) -> *mut FsHandle {
153 if master_key.is_null() || master_key_len == 0 {
154 return ptr::null_mut();
155 }
156 let path_str = match unsafe { unsafe_cstr_to_str(path) } {
157 Some(s) => s,
158 None => return ptr::null_mut(),
159 };
160 let key_slice = unsafe { std::slice::from_raw_parts(master_key, master_key_len) };
161
162 let bs = if block_size == 0 {
163 DEFAULT_BLOCK_SIZE
164 } else {
165 block_size as usize
166 };
167
168 let store = if initialize != 0 {
169 match DeviceBlockStore::initialize(path_str, bs, total_blocks) {
170 Ok(s) => Arc::new(s),
171 Err(_) => return ptr::null_mut(),
172 }
173 } else {
174 match DeviceBlockStore::open(path_str, bs, total_blocks) {
175 Ok(s) => Arc::new(s),
176 Err(_) => return ptr::null_mut(),
177 }
178 };
179
180 let crypto = match ChaChaEngine::new(key_slice) {
181 Ok(c) => Arc::new(c),
182 Err(_) => return ptr::null_mut(),
183 };
184
185 let core = FilesystemCore::new(store, crypto);
186 let handle = Box::new(FsHandle { core });
187 Box::into_raw(handle)
188}
189
190#[no_mangle]
209pub unsafe extern "C" fn fs_create_network(
210 addr: *const c_char,
211 server_name: *const c_char,
212 ca_cert_path: *const c_char,
213 cache_blocks: u32,
214 master_key: *const u8,
215 master_key_len: usize,
216) -> *mut FsHandle {
217 if master_key.is_null() || master_key_len == 0 {
218 return ptr::null_mut();
219 }
220 let addr_str = match unsafe { unsafe_cstr_to_str(addr) } {
221 Some(s) => s,
222 None => return ptr::null_mut(),
223 };
224 let sni_str = match unsafe { unsafe_cstr_to_str(server_name) } {
225 Some(s) => s,
226 None => return ptr::null_mut(),
227 };
228 let ca_str = match unsafe { unsafe_cstr_to_str(ca_cert_path) } {
229 Some(s) => s,
230 None => return ptr::null_mut(),
231 };
232 let key_slice = unsafe { std::slice::from_raw_parts(master_key, master_key_len) };
233
234 let config = NetworkBlockStoreConfig::new(addr_str, sni_str)
235 .ca_cert(ca_str)
236 .auth_token(key_slice);
237
238 let net_store = match crate::network_store::NetworkBlockStore::from_config(config) {
239 Ok(s) => s,
240 Err(_) => return ptr::null_mut(),
241 };
242
243 let cap = if cache_blocks == 0 {
244 256
245 } else {
246 cache_blocks as usize
247 };
248 let store = Arc::new(CachedBlockStore::new(net_store, cap));
249
250 let crypto = match ChaChaEngine::new(key_slice) {
251 Ok(c) => Arc::new(c),
252 Err(_) => return ptr::null_mut(),
253 };
254
255 let core = FilesystemCore::new(store, crypto);
256 let handle = Box::new(FsHandle { core });
257 Box::into_raw(handle)
258}
259
260#[no_mangle]
266pub unsafe extern "C" fn fs_destroy(handle: *mut FsHandle) {
267 if !handle.is_null() {
268 unsafe {
269 drop(Box::from_raw(handle));
270 }
271 }
272}
273
274#[no_mangle]
281pub unsafe extern "C" fn fs_init_filesystem(handle: *mut FsHandle) -> i32 {
282 let Some(h) = (unsafe { handle.as_mut() }) else {
283 return FsErrorCode::InvalidArgument as i32;
284 };
285 match h.core.init_filesystem() {
286 Ok(()) => FsErrorCode::Ok as i32,
287 Err(ref e) => FsErrorCode::from(e) as i32,
288 }
289}
290
291#[no_mangle]
296pub unsafe extern "C" fn fs_open(handle: *mut FsHandle) -> i32 {
297 let Some(h) = (unsafe { handle.as_mut() }) else {
298 return FsErrorCode::InvalidArgument as i32;
299 };
300 match h.core.open() {
301 Ok(()) => FsErrorCode::Ok as i32,
302 Err(ref e) => FsErrorCode::from(e) as i32,
303 }
304}
305
306#[no_mangle]
314pub unsafe extern "C" fn fs_create_file(handle: *mut FsHandle, name: *const c_char) -> i32 {
315 let (h, name_str) = match validate_handle_and_name(handle, name) {
316 Ok(v) => v,
317 Err(code) => return code,
318 };
319 match h.core.create_file(name_str) {
320 Ok(()) => FsErrorCode::Ok as i32,
321 Err(ref e) => FsErrorCode::from(e) as i32,
322 }
323}
324
325#[no_mangle]
331pub unsafe extern "C" fn fs_write_file(
332 handle: *mut FsHandle,
333 name: *const c_char,
334 offset: u64,
335 data: *const u8,
336 data_len: usize,
337) -> i32 {
338 let (h, name_str) = match validate_handle_and_name(handle, name) {
339 Ok(v) => v,
340 Err(code) => return code,
341 };
342 if data.is_null() && data_len > 0 {
343 return FsErrorCode::InvalidArgument as i32;
344 }
345 let slice = if data_len > 0 {
346 unsafe { std::slice::from_raw_parts(data, data_len) }
347 } else {
348 &[]
349 };
350 match h.core.write_file(name_str, offset, slice) {
351 Ok(()) => FsErrorCode::Ok as i32,
352 Err(ref e) => FsErrorCode::from(e) as i32,
353 }
354}
355
356#[no_mangle]
366pub unsafe extern "C" fn fs_read_file(
367 handle: *mut FsHandle,
368 name: *const c_char,
369 offset: u64,
370 len: usize,
371 out_buf: *mut u8,
372 out_len: *mut usize,
373) -> i32 {
374 let (h, name_str) = match validate_handle_and_name(handle, name) {
375 Ok(v) => v,
376 Err(code) => return code,
377 };
378 if out_buf.is_null() || out_len.is_null() {
379 return FsErrorCode::InvalidArgument as i32;
380 }
381
382 match h.core.read_file(name_str, offset, len) {
383 Ok(data) => {
384 let buf_capacity = len;
385 if data.len() > buf_capacity {
386 unsafe { *out_len = data.len() };
387 return FsErrorCode::BufferTooSmall as i32;
388 }
389 unsafe {
390 ptr::copy_nonoverlapping(data.as_ptr(), out_buf, data.len());
391 *out_len = data.len();
392 }
393 FsErrorCode::Ok as i32
394 }
395 Err(ref e) => FsErrorCode::from(e) as i32,
396 }
397}
398
399#[no_mangle]
410pub unsafe extern "C" fn fs_list_root(handle: *mut FsHandle, out_error: *mut i32) -> *mut c_char {
411 let Some(h) = (unsafe { handle.as_mut() }) else {
412 if !out_error.is_null() {
413 unsafe { *out_error = FsErrorCode::InvalidArgument as i32 };
414 }
415 return ptr::null_mut();
416 };
417
418 match h.core.list_directory("") {
419 Ok(entries) => {
420 let json = match serde_json::to_string(&entries) {
421 Ok(j) => j,
422 Err(_) => {
423 if !out_error.is_null() {
424 unsafe { *out_error = FsErrorCode::InternalError as i32 };
425 }
426 return ptr::null_mut();
427 }
428 };
429 if !out_error.is_null() {
430 unsafe { *out_error = FsErrorCode::Ok as i32 };
431 }
432 match CString::new(json) {
433 Ok(cs) => cs.into_raw(),
434 Err(_) => {
435 if !out_error.is_null() {
436 unsafe { *out_error = FsErrorCode::InternalError as i32 };
437 }
438 ptr::null_mut()
439 }
440 }
441 }
442 Err(ref e) => {
443 if !out_error.is_null() {
444 unsafe { *out_error = FsErrorCode::from(e) as i32 };
445 }
446 ptr::null_mut()
447 }
448 }
449}
450
451#[no_mangle]
463pub unsafe extern "C" fn fs_list_dir(
464 handle: *mut FsHandle,
465 path: *const c_char,
466 out_error: *mut i32,
467) -> *mut c_char {
468 let Some(h) = (unsafe { handle.as_mut() }) else {
469 if !out_error.is_null() {
470 unsafe { *out_error = FsErrorCode::InvalidArgument as i32 };
471 }
472 return ptr::null_mut();
473 };
474 let path_str = match unsafe { unsafe_cstr_to_str(path) } {
475 Some(s) => s,
476 None => {
477 if !out_error.is_null() {
478 unsafe { *out_error = FsErrorCode::InvalidArgument as i32 };
479 }
480 return ptr::null_mut();
481 }
482 };
483
484 match h.core.list_directory(path_str) {
485 Ok(entries) => {
486 let json = match serde_json::to_string(&entries) {
487 Ok(j) => j,
488 Err(_) => {
489 if !out_error.is_null() {
490 unsafe { *out_error = FsErrorCode::InternalError as i32 };
491 }
492 return ptr::null_mut();
493 }
494 };
495 if !out_error.is_null() {
496 unsafe { *out_error = FsErrorCode::Ok as i32 };
497 }
498 match CString::new(json) {
499 Ok(cs) => cs.into_raw(),
500 Err(_) => {
501 if !out_error.is_null() {
502 unsafe { *out_error = FsErrorCode::InternalError as i32 };
503 }
504 ptr::null_mut()
505 }
506 }
507 }
508 Err(ref e) => {
509 if !out_error.is_null() {
510 unsafe { *out_error = FsErrorCode::from(e) as i32 };
511 }
512 ptr::null_mut()
513 }
514 }
515}
516
517#[no_mangle]
525pub unsafe extern "C" fn fs_create_dir(handle: *mut FsHandle, name: *const c_char) -> i32 {
526 let (h, name_str) = match validate_handle_and_name(handle, name) {
527 Ok(v) => v,
528 Err(code) => return code,
529 };
530 match h.core.create_directory(name_str) {
531 Ok(()) => FsErrorCode::Ok as i32,
532 Err(ref e) => FsErrorCode::from(e) as i32,
533 }
534}
535
536#[no_mangle]
541pub unsafe extern "C" fn fs_remove_file(handle: *mut FsHandle, name: *const c_char) -> i32 {
542 let (h, name_str) = match validate_handle_and_name(handle, name) {
543 Ok(v) => v,
544 Err(code) => return code,
545 };
546 match h.core.remove_file(name_str) {
547 Ok(()) => FsErrorCode::Ok as i32,
548 Err(ref e) => FsErrorCode::from(e) as i32,
549 }
550}
551
552#[no_mangle]
557pub unsafe extern "C" fn fs_rename(
558 handle: *mut FsHandle,
559 old_name: *const c_char,
560 new_name: *const c_char,
561) -> i32 {
562 let Some(h) = (unsafe { handle.as_mut() }) else {
563 return FsErrorCode::InvalidArgument as i32;
564 };
565 let old_str = match unsafe_cstr_to_str(old_name) {
566 Some(s) => s,
567 None => return FsErrorCode::InvalidArgument as i32,
568 };
569 let new_str = match unsafe_cstr_to_str(new_name) {
570 Some(s) => s,
571 None => return FsErrorCode::InvalidArgument as i32,
572 };
573 match h.core.rename(old_str, new_str) {
574 Ok(()) => FsErrorCode::Ok as i32,
575 Err(ref e) => FsErrorCode::from(e) as i32,
576 }
577}
578
579#[no_mangle]
587pub unsafe extern "C" fn fs_flush(handle: *mut FsHandle) -> i32 {
588 let Some(h) = (unsafe { handle.as_mut() }) else {
589 return FsErrorCode::InvalidArgument as i32;
590 };
591 match h.core.flush() {
592 Ok(()) => FsErrorCode::Ok as i32,
593 Err(ref e) => FsErrorCode::from(e) as i32,
594 }
595}
596
597#[no_mangle]
602pub unsafe extern "C" fn fs_sync(handle: *mut FsHandle) -> i32 {
603 let Some(h) = (unsafe { handle.as_mut() }) else {
604 return FsErrorCode::InvalidArgument as i32;
605 };
606 match h.core.sync() {
607 Ok(()) => FsErrorCode::Ok as i32,
608 Err(ref e) => FsErrorCode::from(e) as i32,
609 }
610}
611
612#[no_mangle]
624pub unsafe extern "C" fn fs_stat(
625 handle: *mut FsHandle,
626 name: *const c_char,
627 out_size: *mut u64,
628 out_kind: *mut i32,
629 out_inode_id: *mut u64,
630) -> i32 {
631 let (h, name_str) = match validate_handle_and_name(handle, name) {
632 Ok(v) => v,
633 Err(code) => return code,
634 };
635 if out_size.is_null() || out_kind.is_null() || out_inode_id.is_null() {
636 return FsErrorCode::InvalidArgument as i32;
637 }
638 match h.core.stat(name_str) {
639 Ok(entry) => {
640 unsafe {
641 *out_size = entry.size;
642 *out_kind = match entry.kind {
643 crate::model::InodeKind::File => 0,
644 crate::model::InodeKind::Directory => 1,
645 };
646 *out_inode_id = entry.inode_id;
647 }
648 FsErrorCode::Ok as i32
649 }
650 Err(ref e) => FsErrorCode::from(e) as i32,
651 }
652}
653
654#[no_mangle]
659pub unsafe extern "C" fn fs_scrub_free_blocks(handle: *mut FsHandle) -> i32 {
660 let Some(h) = (unsafe { handle.as_mut() }) else {
661 return FsErrorCode::InvalidArgument as i32;
662 };
663 match h.core.scrub_free_blocks() {
664 Ok(()) => FsErrorCode::Ok as i32,
665 Err(ref e) => FsErrorCode::from(e) as i32,
666 }
667}
668
669#[no_mangle]
674pub unsafe extern "C" fn fs_free_string(s: *mut c_char) {
675 if !s.is_null() {
676 unsafe {
677 drop(CString::from_raw(s));
678 }
679 }
680}
681
682unsafe fn unsafe_cstr_to_str<'a>(ptr: *const c_char) -> Option<&'a str> {
687 if ptr.is_null() {
688 return None;
689 }
690 unsafe { CStr::from_ptr(ptr) }.to_str().ok()
691}
692
693unsafe fn validate_handle_and_name<'a>(
694 handle: *mut FsHandle,
695 name: *const c_char,
696) -> Result<(&'a mut FsHandle, &'a str), i32> {
697 let h = unsafe { handle.as_mut() }.ok_or(FsErrorCode::InvalidArgument as i32)?;
698 let name_str =
699 unsafe { unsafe_cstr_to_str(name) }.ok_or(FsErrorCode::InvalidArgument as i32)?;
700 Ok((h, name_str))
701}