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]
242pub unsafe extern "C" fn fs_create_file(handle: *mut FsHandle, name: *const c_char) -> i32 {
243 let (h, name_str) = match validate_handle_and_name(handle, name) {
244 Ok(v) => v,
245 Err(code) => return code,
246 };
247 match h.core.create_file(name_str) {
248 Ok(()) => FsErrorCode::Ok as i32,
249 Err(ref e) => FsErrorCode::from(e) as i32,
250 }
251}
252
253#[no_mangle]
259pub unsafe extern "C" fn fs_write_file(
260 handle: *mut FsHandle,
261 name: *const c_char,
262 offset: u64,
263 data: *const u8,
264 data_len: usize,
265) -> i32 {
266 let (h, name_str) = match validate_handle_and_name(handle, name) {
267 Ok(v) => v,
268 Err(code) => return code,
269 };
270 if data.is_null() && data_len > 0 {
271 return FsErrorCode::InvalidArgument as i32;
272 }
273 let slice = if data_len > 0 {
274 unsafe { std::slice::from_raw_parts(data, data_len) }
275 } else {
276 &[]
277 };
278 match h.core.write_file(name_str, offset, slice) {
279 Ok(()) => FsErrorCode::Ok as i32,
280 Err(ref e) => FsErrorCode::from(e) as i32,
281 }
282}
283
284#[no_mangle]
294pub unsafe extern "C" fn fs_read_file(
295 handle: *mut FsHandle,
296 name: *const c_char,
297 offset: u64,
298 len: usize,
299 out_buf: *mut u8,
300 out_len: *mut usize,
301) -> i32 {
302 let (h, name_str) = match validate_handle_and_name(handle, name) {
303 Ok(v) => v,
304 Err(code) => return code,
305 };
306 if out_buf.is_null() || out_len.is_null() {
307 return FsErrorCode::InvalidArgument as i32;
308 }
309
310 match h.core.read_file(name_str, offset, len) {
311 Ok(data) => {
312 let buf_capacity = len;
313 if data.len() > buf_capacity {
314 unsafe { *out_len = data.len() };
315 return FsErrorCode::BufferTooSmall as i32;
316 }
317 unsafe {
318 ptr::copy_nonoverlapping(data.as_ptr(), out_buf, data.len());
319 *out_len = data.len();
320 }
321 FsErrorCode::Ok as i32
322 }
323 Err(ref e) => FsErrorCode::from(e) as i32,
324 }
325}
326
327#[no_mangle]
338pub unsafe extern "C" fn fs_list_root(handle: *mut FsHandle, out_error: *mut i32) -> *mut c_char {
339 let Some(h) = (unsafe { handle.as_mut() }) else {
340 if !out_error.is_null() {
341 unsafe { *out_error = FsErrorCode::InvalidArgument as i32 };
342 }
343 return ptr::null_mut();
344 };
345
346 match h.core.list_directory("") {
347 Ok(entries) => {
348 let json = match serde_json::to_string(&entries) {
349 Ok(j) => j,
350 Err(_) => {
351 if !out_error.is_null() {
352 unsafe { *out_error = FsErrorCode::InternalError as i32 };
353 }
354 return ptr::null_mut();
355 }
356 };
357 if !out_error.is_null() {
358 unsafe { *out_error = FsErrorCode::Ok as i32 };
359 }
360 match CString::new(json) {
361 Ok(cs) => cs.into_raw(),
362 Err(_) => {
363 if !out_error.is_null() {
364 unsafe { *out_error = FsErrorCode::InternalError as i32 };
365 }
366 ptr::null_mut()
367 }
368 }
369 }
370 Err(ref e) => {
371 if !out_error.is_null() {
372 unsafe { *out_error = FsErrorCode::from(e) as i32 };
373 }
374 ptr::null_mut()
375 }
376 }
377}
378
379#[no_mangle]
391pub unsafe extern "C" fn fs_list_dir(
392 handle: *mut FsHandle,
393 path: *const c_char,
394 out_error: *mut i32,
395) -> *mut c_char {
396 let Some(h) = (unsafe { handle.as_mut() }) else {
397 if !out_error.is_null() {
398 unsafe { *out_error = FsErrorCode::InvalidArgument as i32 };
399 }
400 return ptr::null_mut();
401 };
402 let path_str = match unsafe { unsafe_cstr_to_str(path) } {
403 Some(s) => s,
404 None => {
405 if !out_error.is_null() {
406 unsafe { *out_error = FsErrorCode::InvalidArgument as i32 };
407 }
408 return ptr::null_mut();
409 }
410 };
411
412 match h.core.list_directory(path_str) {
413 Ok(entries) => {
414 let json = match serde_json::to_string(&entries) {
415 Ok(j) => j,
416 Err(_) => {
417 if !out_error.is_null() {
418 unsafe { *out_error = FsErrorCode::InternalError as i32 };
419 }
420 return ptr::null_mut();
421 }
422 };
423 if !out_error.is_null() {
424 unsafe { *out_error = FsErrorCode::Ok as i32 };
425 }
426 match CString::new(json) {
427 Ok(cs) => cs.into_raw(),
428 Err(_) => {
429 if !out_error.is_null() {
430 unsafe { *out_error = FsErrorCode::InternalError as i32 };
431 }
432 ptr::null_mut()
433 }
434 }
435 }
436 Err(ref e) => {
437 if !out_error.is_null() {
438 unsafe { *out_error = FsErrorCode::from(e) as i32 };
439 }
440 ptr::null_mut()
441 }
442 }
443}
444
445#[no_mangle]
453pub unsafe extern "C" fn fs_create_dir(handle: *mut FsHandle, name: *const c_char) -> i32 {
454 let (h, name_str) = match validate_handle_and_name(handle, name) {
455 Ok(v) => v,
456 Err(code) => return code,
457 };
458 match h.core.create_directory(name_str) {
459 Ok(()) => FsErrorCode::Ok as i32,
460 Err(ref e) => FsErrorCode::from(e) as i32,
461 }
462}
463
464#[no_mangle]
469pub unsafe extern "C" fn fs_remove_file(handle: *mut FsHandle, name: *const c_char) -> i32 {
470 let (h, name_str) = match validate_handle_and_name(handle, name) {
471 Ok(v) => v,
472 Err(code) => return code,
473 };
474 match h.core.remove_file(name_str) {
475 Ok(()) => FsErrorCode::Ok as i32,
476 Err(ref e) => FsErrorCode::from(e) as i32,
477 }
478}
479
480#[no_mangle]
485pub unsafe extern "C" fn fs_rename(
486 handle: *mut FsHandle,
487 old_name: *const c_char,
488 new_name: *const c_char,
489) -> i32 {
490 let Some(h) = (unsafe { handle.as_mut() }) else {
491 return FsErrorCode::InvalidArgument as i32;
492 };
493 let old_str = match unsafe_cstr_to_str(old_name) {
494 Some(s) => s,
495 None => return FsErrorCode::InvalidArgument as i32,
496 };
497 let new_str = match unsafe_cstr_to_str(new_name) {
498 Some(s) => s,
499 None => return FsErrorCode::InvalidArgument as i32,
500 };
501 match h.core.rename(old_str, new_str) {
502 Ok(()) => FsErrorCode::Ok as i32,
503 Err(ref e) => FsErrorCode::from(e) as i32,
504 }
505}
506
507#[no_mangle]
512pub unsafe extern "C" fn fs_sync(handle: *mut FsHandle) -> i32 {
513 let Some(h) = (unsafe { handle.as_mut() }) else {
514 return FsErrorCode::InvalidArgument as i32;
515 };
516 match h.core.sync() {
517 Ok(()) => FsErrorCode::Ok as i32,
518 Err(ref e) => FsErrorCode::from(e) as i32,
519 }
520}
521
522#[no_mangle]
527pub unsafe extern "C" fn fs_free_string(s: *mut c_char) {
528 if !s.is_null() {
529 unsafe {
530 drop(CString::from_raw(s));
531 }
532 }
533}
534
535unsafe fn unsafe_cstr_to_str<'a>(ptr: *const c_char) -> Option<&'a str> {
540 if ptr.is_null() {
541 return None;
542 }
543 unsafe { CStr::from_ptr(ptr) }.to_str().ok()
544}
545
546unsafe fn validate_handle_and_name<'a>(
547 handle: *mut FsHandle,
548 name: *const c_char,
549) -> Result<(&'a mut FsHandle, &'a str), i32> {
550 let h = unsafe { handle.as_mut() }.ok_or(FsErrorCode::InvalidArgument as i32)?;
551 let name_str =
552 unsafe { unsafe_cstr_to_str(name) }.ok_or(FsErrorCode::InvalidArgument as i32)?;
553 Ok((h, name_str))
554}