1use crate::{XilaFileIdentifier, XilaFileSystemMode, XilaFileSystemOpen, XilaFileSystemStatus};
2use abi_context::{FileIdentifier, get_instance as get_context_instance};
3use alloc::{borrow::ToOwned, ffi::CString};
4use core::{
5 ffi::{CStr, c_char},
6 ptr::null_mut,
7};
8use file_system::{AccessFlags, CreateFlags, Error, Flags, Path, StateFlags};
9
10use log::debug;
11use virtual_file_system::{
12 SynchronousDirectory, SynchronousFile, get_instance as get_file_system_instance,
13};
14
15use super::{XilaFileKind, XilaFileSystemInode, XilaFileSystemSize, into_u32};
16
17#[unsafe(no_mangle)]
23pub unsafe extern "C" fn xila_file_system_open_at(
24 directory: XilaFileIdentifier,
25 relative_path: *const c_char,
26 is_directory: bool,
27 mode: XilaFileSystemMode,
28 open: XilaFileSystemOpen,
29 status: XilaFileSystemStatus,
30 out: *mut XilaFileIdentifier,
31) -> u32 {
32 into_u32(move || unsafe {
33 if relative_path.is_null() || out.is_null() {
34 Err(Error::InvalidParameter)?;
35 }
36
37 let relative_path = CStr::from_ptr(relative_path)
38 .to_str()
39 .map_err(|_| Error::InvalidParameter)?;
40
41 let context = get_context_instance();
42
43 let task = context.get_current_task_identifier();
44
45 let virtual_file_system = get_file_system_instance();
46
47 let (parent_identifier, full_path) = if relative_path == "/" {
48 (None, Path::ROOT.to_owned())
49 } else {
50 let identifier: FileIdentifier = directory.try_into()?;
51 let path = context
52 .resolve_path(task, identifier, relative_path)
53 .unwrap();
54 (Some(identifier), path)
56 };
57
58 if is_directory {
59 let directory = SynchronousDirectory::open(virtual_file_system, task, &full_path)?;
60
61 *out = context
62 .insert_directory(task, parent_identifier, relative_path, directory)
63 .ok_or(Error::InvalidIdentifier)?
64 .into();
65 } else {
66 let mode = AccessFlags::from_bits_truncate(mode);
67 let open = CreateFlags::from_bits_truncate(open);
68 let status = StateFlags::from_bits_truncate(status);
69
70 let flags = Flags::new(mode, Some(open), Some(status));
71
72 let file = SynchronousFile::open(virtual_file_system, task, &full_path, flags)?;
73
74 *out = context
75 .insert_file(task, file, None)
76 .ok_or(Error::InvalidIdentifier)?
77 .into();
78 }
79
80 Ok(())
81 })
82}
83
84#[unsafe(no_mangle)]
90pub unsafe extern "C" fn xila_file_system_open_directory(
91 path: *const c_char,
92 directory: *mut XilaFileIdentifier,
93) -> u32 {
94 unsafe {
95 into_u32(move || {
96 if path.is_null() || directory.is_null() {
97 Err(Error::InvalidParameter)?;
98 }
99
100 let path = CStr::from_ptr(path)
101 .to_str()
102 .map_err(|_| Error::InvalidParameter)?;
103
104 let context = get_context_instance();
105
106 let task = context.get_current_task_identifier();
107
108 debug!("Opening directory {path:?} for task {task:?}");
109
110 let d = SynchronousDirectory::open(get_file_system_instance(), task, path)?;
111
112 *directory = context
113 .insert_directory(task, None, path, d)
114 .ok_or(Error::InvalidIdentifier)?
115 .into();
116 Ok(())
117 })
118 }
119}
120
121#[unsafe(no_mangle)]
127pub unsafe extern "C" fn xila_file_system_read_directory(
128 directory: XilaFileIdentifier,
129 entry_name: *mut *const c_char,
130 entry_type: *mut XilaFileKind,
131 entry_size: *mut XilaFileSystemSize,
132 entry_inode: *mut XilaFileSystemInode,
133) -> u32 {
134 unsafe {
135 into_u32(move || {
136 let task = get_context_instance().get_current_task_identifier();
137
138 debug!("Reading directory {directory:?} for task {task:?}");
139
140 let entry = get_context_instance()
141 .perform_operation_on_directory(directory.try_into()?, SynchronousDirectory::read)
142 .ok_or(Error::InvalidIdentifier)??;
143
144 if let Some(entry) = entry {
145 *entry_name = CString::new(entry.name.as_str()).unwrap().into_raw();
146 *entry_type = entry.kind.into();
147 *entry_size = entry.size;
148 *entry_inode = entry.inode;
149 } else {
150 *entry_name = null_mut();
151 }
152
153 Ok(())
154 })
155 }
156}
157
158#[unsafe(no_mangle)]
159pub extern "C" fn xila_file_system_close_directory(directory: XilaFileIdentifier) -> u32 {
160 into_u32(move || {
161 let d = get_context_instance()
162 .remove_directory(directory.try_into()?)
163 .ok_or(Error::InvalidIdentifier)?;
164
165 d.close(get_file_system_instance())?;
166
167 Ok(())
168 })
169}
170
171#[unsafe(no_mangle)]
172pub extern "C" fn xila_file_system_rewind_directory(directory: XilaFileIdentifier) -> u32 {
173 into_u32(move || {
174 debug!("Rewinding directory {directory:?} ");
175
176 get_context_instance()
177 .perform_operation_on_directory(directory.try_into()?, SynchronousDirectory::rewind)
178 .ok_or(Error::InvalidIdentifier)??;
179
180 Ok(())
181 })
182}
183
184#[unsafe(no_mangle)]
185pub extern "C" fn xila_file_system_directory_set_position(
186 directory: XilaFileIdentifier,
187 offset: XilaFileSystemSize,
188) -> u32 {
189 into_u32(move || {
190 debug!("Setting position in directory {directory:?} to offset {offset}");
191
192 get_context_instance()
193 .perform_operation_on_directory(directory.try_into()?, |d| d.set_position(offset))
194 .ok_or(Error::InvalidIdentifier)??;
195
196 Ok(())
197 })
198}
199
200#[cfg(test)]
201mod tests {
202 extern crate std;
203
204 use super::*;
205 use abi_context::get_instance as get_context_instance;
206 use alloc::{ffi::CString, format, vec::Vec};
207 use file_system::{AccessFlags, CreateFlags, MemoryDevice, PathOwned};
208 use task::{TaskIdentifier, test};
209 use virtual_file_system::{Directory, File, VirtualFileSystem};
210
211 async fn initialize() -> (TaskIdentifier, &'static VirtualFileSystem) {
212 if !log::is_initialized() {
213 log::initialize(&drivers_std::log::Logger).unwrap();
214 }
215
216 let users_manager = users::initialize();
217
218 let time_manager = time::initialize(&drivers_std::devices::TimeDevice).unwrap();
219
220 let task_manager = task::initialize();
221
222 let task = task::get_instance().get_current_task_identifier().await;
223
224 let device = MemoryDevice::<512>::new_static(1024 * 512);
225
226 let cache_size = 256;
227
228 little_fs::FileSystem::format(device, cache_size).unwrap();
229 let file_system = little_fs::FileSystem::new(device, cache_size).unwrap();
230
231 let virtual_file_system =
232 virtual_file_system::initialize(task_manager, users_manager, time_manager, file_system)
233 .unwrap();
234
235 (task, virtual_file_system)
236 }
237
238 #[test]
239 async fn test_null_pointer_handling() {
240 let (_task, _vfs) = initialize().await;
242
243 let context = get_context_instance();
244
245 let mut directory_id: XilaFileIdentifier = 0;
247 let result = context
248 .call_abi(async || unsafe {
249 xila_file_system_open_directory(core::ptr::null(), &mut directory_id)
250 })
251 .await;
252 assert_ne!(result, 0, "Opening directory with null path should fail");
253
254 let invalid_handle: XilaFileIdentifier = 9999;
256 let result = context
257 .call_abi(async || unsafe {
258 xila_file_system_read_directory(
259 invalid_handle,
260 core::ptr::null_mut(),
261 core::ptr::null_mut(),
262 core::ptr::null_mut(),
263 core::ptr::null_mut(),
264 )
265 })
266 .await;
267 assert_ne!(
268 result, 0,
269 "Reading directory with null pointers should fail"
270 );
271 }
272
273 #[test]
274 async fn test_invalid_handle_operations() {
275 initialize().await; let invalid_handle: XilaFileIdentifier = 9999;
278 let context = get_context_instance();
279
280 let result = context
282 .call_abi(|| async { xila_file_system_close_directory(invalid_handle) })
283 .await;
284 assert_ne!(result, 0, "Closing invalid directory handle should fail");
285
286 let result = context
288 .call_abi(|| async { xila_file_system_rewind_directory(invalid_handle) })
289 .await;
290 assert_ne!(result, 0, "Rewinding invalid directory handle should fail");
291
292 let result = context
294 .call_abi(|| async { xila_file_system_directory_set_position(invalid_handle, 0) })
295 .await;
296 assert_ne!(
297 result, 0,
298 "Setting position on invalid directory handle should fail"
299 );
300 }
301
302 #[test]
303 async fn test_read_directory_parameter_validation() {
304 initialize().await; let invalid_handle: XilaFileIdentifier = 0;
308 let mut entry_name: *const c_char = core::ptr::null();
309 let mut entry_type: XilaFileKind = XilaFileKind::File;
310 let mut entry_size: XilaFileSystemSize = 0;
311 let mut entry_inode: XilaFileSystemInode = 0;
312 let context = get_context_instance();
313
314 let result = context
316 .call_abi(async || unsafe {
317 xila_file_system_read_directory(
318 invalid_handle,
319 &mut entry_name,
320 &mut entry_type,
321 &mut entry_size,
322 &mut entry_inode,
323 )
324 })
325 .await;
326 assert_ne!(result, 0, "Reading from invalid handle should fail");
327 }
328
329 #[test]
330 async fn test_set_position_boundary_values() {
331 initialize().await; let invalid_handle: XilaFileIdentifier = 9999;
334 let context = get_context_instance();
335
336 let result = context
338 .call_abi(|| async {
339 xila_file_system_directory_set_position(invalid_handle, u64::MAX)
340 })
341 .await;
342 assert_ne!(
343 result, 0,
344 "Setting position with max value on invalid handle should fail"
345 );
346
347 let result = context
349 .call_abi(|| async { xila_file_system_directory_set_position(invalid_handle, 0) })
350 .await;
351 assert_ne!(
352 result, 0,
353 "Setting position with zero on invalid handle should fail"
354 );
355 }
356
357 #[test]
358 async fn test_open_directory_valid_path() {
359 let (_task, _vfs) = initialize().await;
360 let context = get_context_instance();
361
362 let path = CString::new("/").unwrap();
363 let mut directory_id: XilaFileIdentifier = 0;
364
365 let result = context
366 .call_abi(async || unsafe {
367 xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
368 })
369 .await;
370 assert_eq!(result, 0, "Opening root directory should succeed");
371 assert_ne!(directory_id, 0, "Directory identifier should be non-zero");
372
373 let close_result = context
375 .call_abi(|| async { xila_file_system_close_directory(directory_id) })
376 .await;
377 assert_eq!(close_result, 0, "Closing directory should succeed");
378 }
379
380 #[test]
381 async fn test_open_directory_invalid_path() {
382 let (_task, _vfs) = initialize().await;
383 let context = get_context_instance();
384
385 let path = CString::new("/nonexistent").unwrap();
386 let mut directory_id: XilaFileIdentifier = 0;
387
388 let result = context
389 .call_abi(async || unsafe {
390 xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
391 })
392 .await;
393
394 assert_ne!(result, 0, "Opening nonexistent directory should fail");
395 }
396
397 #[test]
398 async fn test_open_directory_null_path() {
399 let (_task, _vfs) = initialize().await;
400 let context = get_context_instance();
401
402 let mut directory_id: XilaFileIdentifier = 0;
403
404 let result = context
405 .call_abi(async || unsafe {
406 xila_file_system_open_directory(core::ptr::null(), &mut directory_id)
407 })
408 .await;
409
410 assert_ne!(result, 0, "Opening directory with null path should fail");
411 }
412
413 #[test]
414 async fn test_read_directory_entries() {
415 let (_task, vfs) = initialize().await;
416 let task = _task;
417 let context = get_context_instance();
418
419 Directory::create(vfs, task, "/test_read_directory_entries")
421 .await
422 .unwrap();
423
424 let test_file = File::open(
425 vfs,
426 task,
427 "/test_read_directory_entries.txt",
428 Flags::new(AccessFlags::Write, Some(CreateFlags::Create), None),
429 )
430 .await
431 .unwrap();
432
433 test_file.close(vfs).await.unwrap();
434
435 let path = CString::new("/").unwrap();
437 let mut directory_id: XilaFileIdentifier = 0;
438
439 let open_result = context
440 .call_abi(async || unsafe {
441 xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
442 })
443 .await;
444 assert_eq!(open_result, 0, "Opening root directory should succeed");
445
446 let mut entries_found = Vec::new();
449
450 loop {
452 let mut entry_name: *const c_char = core::ptr::null();
453 let mut entry_type: XilaFileKind = XilaFileKind::File;
454 let mut entry_size: XilaFileSystemSize = 0;
455 let mut entry_inode: XilaFileSystemInode = 0;
456
457 let read_result = context
458 .call_abi(async || unsafe {
459 xila_file_system_read_directory(
460 directory_id,
461 &mut entry_name,
462 &mut entry_type,
463 &mut entry_size,
464 &mut entry_inode,
465 )
466 })
467 .await;
468
469 assert_eq!(read_result, 0, "Reading directory entry should succeed");
470
471 if entry_name.is_null() {
472 break; }
474
475 let name = unsafe { CStr::from_ptr(entry_name).to_string_lossy().into_owned() };
476 entries_found.push((name, entry_type));
477
478 unsafe {
480 let _ = CString::from_raw(entry_name as *mut c_char);
481 }
482 }
483
484 assert!(
486 entries_found.len() >= 2,
487 "Should find at least current and parent directories"
488 );
489
490 let has_current = entries_found.iter().any(|(name, _)| name == ".");
492 let has_parent = entries_found.iter().any(|(name, _)| name == "..");
493
494 assert!(has_current, "Should find current directory entry");
495 assert!(has_parent, "Should find parent directory entry");
496
497 let close_result = context
499 .call_abi(|| async { xila_file_system_close_directory(directory_id) })
500 .await;
501 assert_eq!(close_result, 0, "Closing directory should succeed");
502 }
503
504 #[test]
505 async fn test_read_directory_invalid_handle() {
506 let (_task, _vfs) = initialize().await;
507 let context = get_context_instance();
508
509 let invalid_directory_id: XilaFileIdentifier = 9999;
510 let mut entry_name: *const c_char = core::ptr::null();
511 let mut entry_type: XilaFileKind = XilaFileKind::File;
512 let mut entry_size: XilaFileSystemSize = 0;
513 let mut entry_inode: XilaFileSystemInode = 0;
514
515 let result = context
516 .call_abi(async || unsafe {
517 xila_file_system_read_directory(
518 invalid_directory_id,
519 &mut entry_name,
520 &mut entry_type,
521 &mut entry_size,
522 &mut entry_inode,
523 )
524 })
525 .await;
526
527 assert_ne!(
528 result, 0,
529 "Reading from invalid directory handle should fail"
530 );
531 }
532
533 #[test]
534 async fn test_close_directory_valid_handle() {
535 let (_task, _vfs) = initialize().await;
536 let context = get_context_instance();
537
538 let path = CString::new("/").unwrap();
539 let mut directory_id: XilaFileIdentifier = 0;
540
541 let open_result = context
543 .call_abi(async || unsafe {
544 xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
545 })
546 .await;
547 assert_eq!(open_result, 0, "Opening directory should succeed");
548
549 let close_result = context
551 .call_abi(|| async { xila_file_system_close_directory(directory_id) })
552 .await;
553 assert_eq!(close_result, 0, "Closing directory should succeed");
554 }
555
556 #[test]
557 async fn test_close_directory_invalid_handle() {
558 let (_task, _vfs) = initialize().await;
559 let context = get_context_instance();
560
561 let invalid_directory_id: XilaFileIdentifier = 9999;
562
563 let result = context
564 .call_abi(|| async { xila_file_system_close_directory(invalid_directory_id) })
565 .await;
566 assert_ne!(result, 0, "Closing invalid directory handle should fail");
567 }
568
569 #[test]
570 async fn test_rewind_directory() {
571 let (_task, vfs) = initialize().await;
572 let task = _task;
573 let context = get_context_instance();
574
575 for i in 0..3 {
577 let path = format!("/test_rewind_directory_{i}.txt");
578
579 let test_file = vfs
580 .open(
581 &PathOwned::new(path).unwrap(),
582 file_system::Flags::new(AccessFlags::Write, Some(CreateFlags::Create), None),
583 task,
584 )
585 .await
586 .unwrap();
587 test_file.close(vfs).await.unwrap();
588 }
589
590 let path = CString::new("/").unwrap();
592 let mut directory_id: XilaFileIdentifier = 0;
593
594 let open_result = context
595 .call_abi(async || unsafe {
596 xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
597 })
598 .await;
599 assert_eq!(open_result, 0, "Opening directory should succeed");
600
601 for _ in 0..2 {
603 let mut entry_name: *const c_char = core::ptr::null();
604 let mut entry_type: XilaFileKind = XilaFileKind::File;
605 let mut entry_size: XilaFileSystemSize = 0;
606 let mut entry_inode: XilaFileSystemInode = 0;
607
608 let read_result = context
609 .call_abi(async || unsafe {
610 xila_file_system_read_directory(
611 directory_id,
612 &mut entry_name,
613 &mut entry_type,
614 &mut entry_size,
615 &mut entry_inode,
616 )
617 })
618 .await;
619 assert_eq!(read_result, 0, "Reading directory should succeed");
620
621 if !entry_name.is_null() {
622 unsafe {
623 let _ = CString::from_raw(entry_name as *mut c_char);
624 }
625 }
626 }
627
628 let rewind_result = context
630 .call_abi(|| async { xila_file_system_rewind_directory(directory_id) })
631 .await;
632 assert_eq!(rewind_result, 0, "Rewinding directory should succeed");
633
634 let mut entry_name: *const c_char = core::ptr::null();
636 let mut entry_type: XilaFileKind = XilaFileKind::File;
637 let mut entry_size: XilaFileSystemSize = 0;
638 let mut entry_inode: XilaFileSystemInode = 0;
639
640 let read_result = context
641 .call_abi(async || unsafe {
642 xila_file_system_read_directory(
643 directory_id,
644 &mut entry_name,
645 &mut entry_type,
646 &mut entry_size,
647 &mut entry_inode,
648 )
649 })
650 .await;
651 assert_eq!(
652 read_result, 0,
653 "Reading directory after rewind should succeed"
654 );
655 assert!(!entry_name.is_null(), "Entry name should not be null");
656
657 let name = unsafe { CStr::from_ptr(entry_name).to_string_lossy() };
658 assert_eq!(
659 name, ".",
660 "First entry after rewind should be current directory"
661 );
662
663 unsafe {
664 let _ = CString::from_raw(entry_name as *mut c_char);
665 }
666
667 let close_result = context
669 .call_abi(|| async { xila_file_system_close_directory(directory_id) })
670 .await;
671 assert_eq!(close_result, 0, "Closing directory should succeed");
672 }
673
674 #[test]
675 async fn test_rewind_directory_invalid_handle() {
676 let (_task, _vfs) = initialize().await;
677 let context = get_context_instance();
678
679 let invalid_directory_id: XilaFileIdentifier = 9999;
680
681 let result = context
682 .call_abi(|| async { xila_file_system_rewind_directory(invalid_directory_id) })
683 .await;
684 assert_ne!(result, 0, "Rewinding invalid directory handle should fail");
685 }
686
687 #[test]
688 async fn test_directory_set_position() {
689 let (_task, vfs) = initialize().await;
690 let task = _task;
691 let context = get_context_instance();
692
693 for i in 0..5 {
695 let path = format!("/test_file_{i}.txt");
696
697 let test_file = vfs
698 .open(
699 &PathOwned::new(path).unwrap(),
700 file_system::Flags::new(AccessFlags::Write, Some(CreateFlags::Create), None),
701 task,
702 )
703 .await
704 .unwrap();
705 test_file.close(vfs).await.unwrap();
706 }
707
708 let path = CString::new("/").unwrap();
710 let mut directory_id: XilaFileIdentifier = 0;
711
712 let open_result = context
713 .call_abi(async || unsafe {
714 xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
715 })
716 .await;
717 assert_eq!(open_result, 0, "Opening directory should succeed");
718
719 for _ in 0..3 {
721 let mut entry_name: *const c_char = core::ptr::null();
722 let mut entry_type: XilaFileKind = XilaFileKind::File;
723 let mut entry_size: XilaFileSystemSize = 0;
724 let mut entry_inode: XilaFileSystemInode = 0;
725
726 let read_result = context
727 .call_abi(async || unsafe {
728 xila_file_system_read_directory(
729 directory_id,
730 &mut entry_name,
731 &mut entry_type,
732 &mut entry_size,
733 &mut entry_inode,
734 )
735 })
736 .await;
737 assert_eq!(read_result, 0, "Reading directory should succeed");
738
739 if !entry_name.is_null() {
740 unsafe {
741 let _ = CString::from_raw(entry_name as *mut c_char);
742 }
743 }
744 }
745
746 let set_position_result = context
748 .call_abi(|| async { xila_file_system_directory_set_position(directory_id, 0) })
749 .await;
750 assert_eq!(
751 set_position_result, 0,
752 "Setting directory position should succeed"
753 );
754
755 let mut entry_name: *const c_char = core::ptr::null();
757 let mut entry_type: XilaFileKind = XilaFileKind::File;
758 let mut entry_size: XilaFileSystemSize = 0;
759 let mut entry_inode: XilaFileSystemInode = 0;
760
761 let read_result = context
762 .call_abi(async || unsafe {
763 xila_file_system_read_directory(
764 directory_id,
765 &mut entry_name,
766 &mut entry_type,
767 &mut entry_size,
768 &mut entry_inode,
769 )
770 })
771 .await;
772 assert_eq!(
773 read_result, 0,
774 "Reading directory after position reset should succeed"
775 );
776 assert!(!entry_name.is_null(), "Entry name should not be null");
777
778 let name = unsafe { CStr::from_ptr(entry_name).to_string_lossy() };
779 assert_eq!(
780 name, ".",
781 "First entry after position reset should be current directory"
782 );
783
784 unsafe {
785 let _ = CString::from_raw(entry_name as *mut c_char);
786 }
787
788 let close_result = context
790 .call_abi(|| async { xila_file_system_close_directory(directory_id) })
791 .await;
792 assert_eq!(close_result, 0, "Closing directory should succeed");
793 }
794
795 #[test]
796 async fn test_directory_set_position_invalid_handle() {
797 let (_task, _vfs) = initialize().await;
798 let context = get_context_instance();
799
800 let invalid_directory_id: XilaFileIdentifier = 9999;
801
802 let result = context
803 .call_abi(|| async { xila_file_system_directory_set_position(invalid_directory_id, 0) })
804 .await;
805 assert_ne!(
806 result, 0,
807 "Setting position on invalid directory handle should fail"
808 );
809 }
810
811 #[test]
812 async fn test_directory_operations_sequence() {
813 let (_task, vfs) = initialize().await;
814 let task = _task;
815 let context = get_context_instance();
816
817 Directory::create(vfs, task, "/test_dir").await.unwrap();
819 Directory::create(vfs, task, "/test_dir/subdir")
820 .await
821 .unwrap();
822
823 let test_file = File::open(
824 vfs,
825 task,
826 &"/test_dir/file.txt",
827 file_system::Flags::new(AccessFlags::Write, Some(CreateFlags::Create), None),
828 )
829 .await
830 .unwrap();
831 test_file.close(vfs).await.unwrap();
832
833 let path = CString::new("/test_dir").unwrap();
835 let mut directory_id: XilaFileIdentifier = 0;
836
837 let open_result = context
838 .call_abi(async || unsafe {
839 xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
840 })
841 .await;
842 assert_eq!(open_result, 0, "Opening test directory should succeed");
843
844 let mut entry_count = 0;
846 loop {
847 let mut entry_name: *const c_char = core::ptr::null();
848 let mut entry_type: XilaFileKind = XilaFileKind::File;
849 let mut entry_size: XilaFileSystemSize = 0;
850 let mut entry_inode: XilaFileSystemInode = 0;
851
852 let read_result = context
853 .call_abi(async || unsafe {
854 xila_file_system_read_directory(
855 directory_id,
856 &mut entry_name,
857 &mut entry_type,
858 &mut entry_size,
859 &mut entry_inode,
860 )
861 })
862 .await;
863 assert_eq!(read_result, 0, "Reading directory should succeed");
864
865 if entry_name.is_null() {
866 break;
867 }
868
869 entry_count += 1;
870 unsafe {
871 let _ = CString::from_raw(entry_name as *mut c_char);
872 }
873 }
874
875 assert!(
877 entry_count >= 4,
878 "Should find at least 4 entries in test directory"
879 );
880
881 let rewind_result = context
883 .call_abi(|| async { xila_file_system_rewind_directory(directory_id) })
884 .await;
885 assert_eq!(rewind_result, 0, "Rewinding directory should succeed");
886
887 let mut rewind_count = 0;
888 loop {
889 let mut entry_name: *const c_char = core::ptr::null();
890 let mut entry_type: XilaFileKind = XilaFileKind::File;
891 let mut entry_size: XilaFileSystemSize = 0;
892 let mut entry_inode: XilaFileSystemInode = 0;
893
894 let read_result = context
895 .call_abi(async || unsafe {
896 xila_file_system_read_directory(
897 directory_id,
898 &mut entry_name,
899 &mut entry_type,
900 &mut entry_size,
901 &mut entry_inode,
902 )
903 })
904 .await;
905 assert_eq!(
906 read_result, 0,
907 "Reading directory after rewind should succeed"
908 );
909
910 if entry_name.is_null() {
911 break;
912 }
913
914 rewind_count += 1;
915 unsafe {
916 let _ = CString::from_raw(entry_name as *mut c_char);
917 }
918 }
919
920 assert_eq!(
921 entry_count, rewind_count,
922 "Entry count should be same after rewind"
923 );
924
925 let close_result = context
927 .call_abi(|| async { xila_file_system_close_directory(directory_id) })
928 .await;
929 assert_eq!(close_result, 0, "Closing directory should succeed");
930 }
931
932 #[test]
933 async fn test_directory_operations_error_handling() {
934 initialize().await;
935
936 let context = get_context_instance();
937
938 let mut directory_id: XilaFileIdentifier = 0;
940
941 let result = context
943 .call_abi(async || unsafe {
944 xila_file_system_open_directory(core::ptr::null(), &mut directory_id)
945 })
946 .await;
947 assert_ne!(result, 0, "Null path should cause error");
948
949 let invalid_handle = 0;
951
952 let close_result = context
953 .call_abi(|| async { xila_file_system_close_directory(invalid_handle) })
954 .await;
955 assert_ne!(close_result, 0, "Invalid close should fail");
956
957 let rewind_result = context
958 .call_abi(|| async { xila_file_system_rewind_directory(invalid_handle) })
959 .await;
960 assert_ne!(rewind_result, 0, "Invalid rewind should fail");
961
962 let set_pos_result = context
963 .call_abi(|| async { xila_file_system_directory_set_position(invalid_handle, 0) })
964 .await;
965 assert_ne!(set_pos_result, 0, "Invalid set position should fail");
966
967 let mut entry_name: *const c_char = core::ptr::null();
968 let mut entry_type: XilaFileKind = XilaFileKind::File;
969 let mut entry_size: XilaFileSystemSize = 0;
970 let mut entry_inode: XilaFileSystemInode = 0;
971
972 let read_result = context
973 .call_abi(async || unsafe {
974 xila_file_system_read_directory(
975 invalid_handle,
976 &mut entry_name,
977 &mut entry_type,
978 &mut entry_size,
979 &mut entry_inode,
980 )
981 })
982 .await;
983 assert_ne!(read_result, 0, "Invalid read should fail");
984 }
985}