abi/file_system/
directory.rs

1use alloc::ffi::CString;
2use log::Debug;
3
4use core::{
5    ffi::{CStr, c_char},
6    ptr::null_mut,
7};
8
9use file_system::Error;
10use futures::block_on;
11use virtual_file_system::get_instance as get_file_system_instance;
12
13use crate::context::get_instance as get_context_instance;
14
15use super::{
16    XilaFileKind, XilaFileSystemInode, XilaFileSystemSize, XilaUniqueFileIdentifier, into_u32,
17};
18
19/// This function is used to open a directory.
20///
21/// # Safety
22///
23/// This function is unsafe because it dereferences raw pointers.
24#[unsafe(no_mangle)]
25pub unsafe extern "C" fn xila_file_system_open_directory(
26    path: *const c_char,
27    directory: *mut XilaUniqueFileIdentifier,
28) -> u32 {
29    unsafe {
30        into_u32(move || {
31            if path.is_null() || directory.is_null() {
32                Err(Error::InvalidParameter)?;
33            }
34
35            let path = CStr::from_ptr(path)
36                .to_str()
37                .map_err(|_| Error::InvalidParameter)?;
38
39            let task = get_context_instance().get_current_task_identifier();
40
41            Debug!("Opening directory {path:?} for task {task:?}");
42
43            *directory =
44                block_on(get_file_system_instance().open_directory(&path, task))?.into_inner();
45
46            Ok(())
47        })
48    }
49}
50
51/// This function is used to read a directory.
52///
53/// # Safety
54///
55/// This function is unsafe because it dereferences raw pointers.
56#[unsafe(no_mangle)]
57pub unsafe extern "C" fn xila_file_system_read_directory(
58    file: XilaUniqueFileIdentifier,
59    entry_name: *mut *const c_char,
60    entry_type: *mut XilaFileKind,
61    entry_size: *mut XilaFileSystemSize,
62    entry_inode: *mut XilaFileSystemInode,
63) -> u32 {
64    unsafe {
65        into_u32(move || {
66            let task = get_context_instance().get_current_task_identifier();
67
68            Debug!("Reading directory {file:?} for task {task:?}");
69
70            let file = file_system::UniqueFileIdentifier::from_raw(file);
71
72            let entry = block_on(get_file_system_instance().read_directory(file, task))?;
73
74            if let Some(entry) = entry {
75                *entry_name = CString::new(entry.get_name().as_str()).unwrap().into_raw();
76                *entry_type = entry.get_type().into();
77                *entry_size = entry.get_size().as_u64();
78                *entry_inode = entry.get_inode().into();
79            } else {
80                *entry_name = null_mut();
81            }
82
83            Ok(())
84        })
85    }
86}
87
88#[unsafe(no_mangle)]
89pub extern "C" fn xila_file_system_close_directory(directory: XilaUniqueFileIdentifier) -> u32 {
90    into_u32(move || {
91        let task = get_context_instance().get_current_task_identifier();
92
93        let directory = file_system::UniqueFileIdentifier::from_raw(directory);
94
95        Debug!("Closing directory {directory:?} for task {task:?}");
96
97        block_on(get_file_system_instance().close_directory(directory, task))?;
98
99        Ok(())
100    })
101}
102
103#[unsafe(no_mangle)]
104pub extern "C" fn xila_file_system_rewind_directory(directory: XilaUniqueFileIdentifier) -> u32 {
105    into_u32(move || {
106        let task = get_context_instance().get_current_task_identifier();
107
108        let directory = file_system::UniqueFileIdentifier::from_raw(directory);
109
110        Debug!("Rewinding directory {directory:?} for task {task:?}");
111
112        block_on(get_file_system_instance().rewind_directory(directory, task))?;
113
114        Ok(())
115    })
116}
117
118#[unsafe(no_mangle)]
119pub extern "C" fn xila_file_system_directory_set_position(
120    directory: XilaUniqueFileIdentifier,
121    offset: XilaFileSystemSize,
122) -> u32 {
123    into_u32(move || {
124        let task = get_context_instance().get_current_task_identifier();
125
126        let directory = file_system::UniqueFileIdentifier::from_raw(directory);
127
128        Debug!("Setting position in directory {directory:?} to offset {offset} for task {task:?}");
129
130        block_on(get_file_system_instance().set_position_directory(
131            directory,
132            offset.into(),
133            task,
134        ))?;
135
136        Ok(())
137    })
138}
139
140#[cfg(test)]
141mod tests {
142    extern crate std;
143
144    use super::*;
145    use crate::context::get_instance as get_context_instance;
146    use alloc::{ffi::CString, format, vec::Vec};
147    use file_system::{MemoryDevice, Mode, Open, PathOwned, create_device, create_file_system};
148    use task::{TaskIdentifier, test};
149    use virtual_file_system::VirtualFileSystem;
150
151    async fn initialize_test_environment() -> (TaskIdentifier, &'static VirtualFileSystem<'static>)
152    {
153        let _ = users::initialize();
154
155        let _ = time::initialize(create_device!(drivers::native::TimeDriver::new()));
156
157        let task = task::get_instance().get_current_task_identifier().await;
158
159        let device = create_device!(MemoryDevice::<512>::new(1024 * 512));
160
161        let cache_size = 256;
162
163        little_fs::FileSystem::format(device.clone(), cache_size).unwrap();
164        let file_system = little_fs::FileSystem::new(device, cache_size).unwrap();
165
166        let virtual_file_system =
167            virtual_file_system::initialize(create_file_system!(file_system), None).unwrap();
168
169        (task, virtual_file_system)
170    }
171
172    #[test]
173    async fn test_null_pointer_handling() {
174        // Test that functions properly handle null pointers and return appropriate error codes
175        let (_task, _vfs) = initialize_test_environment().await;
176
177        let context = get_context_instance();
178
179        // Test open directory with null path
180        let mut directory_id: XilaUniqueFileIdentifier = 0;
181        let result = context
182            .call_abi(async || unsafe {
183                xila_file_system_open_directory(core::ptr::null(), &mut directory_id)
184            })
185            .await;
186        assert_ne!(result, 0, "Opening directory with null path should fail");
187
188        // Test read directory with null output pointers
189        let invalid_handle: XilaUniqueFileIdentifier = 999999;
190        let result = context
191            .call_abi(async || unsafe {
192                xila_file_system_read_directory(
193                    invalid_handle,
194                    core::ptr::null_mut(),
195                    core::ptr::null_mut(),
196                    core::ptr::null_mut(),
197                    core::ptr::null_mut(),
198                )
199            })
200            .await;
201        assert_ne!(
202            result, 0,
203            "Reading directory with null pointers should fail"
204        );
205    }
206
207    #[test]
208    async fn test_invalid_handle_operations() {
209        initialize_test_environment().await; // Ensure the test environment is initialized
210        // Test operations on invalid directory handles
211        let invalid_handle: XilaUniqueFileIdentifier = 999999;
212        let context = get_context_instance();
213
214        // Test close with invalid handle
215        let result = context
216            .call_abi(|| async { xila_file_system_close_directory(invalid_handle) })
217            .await;
218        assert_ne!(result, 0, "Closing invalid directory handle should fail");
219
220        // Test rewind with invalid handle
221        let result = context
222            .call_abi(|| async { xila_file_system_rewind_directory(invalid_handle) })
223            .await;
224        assert_ne!(result, 0, "Rewinding invalid directory handle should fail");
225
226        // Test set position with invalid handle
227        let result = context
228            .call_abi(|| async { xila_file_system_directory_set_position(invalid_handle, 0) })
229            .await;
230        assert_ne!(
231            result, 0,
232            "Setting position on invalid directory handle should fail"
233        );
234    }
235
236    #[test]
237    async fn test_read_directory_parameter_validation() {
238        initialize_test_environment().await; // Ensure the test environment is initialized
239
240        // Test that read directory validates its parameters properly
241        let invalid_handle: XilaUniqueFileIdentifier = 0;
242        let mut entry_name: *const c_char = core::ptr::null();
243        let mut entry_type: XilaFileKind = XilaFileKind::File;
244        let mut entry_size: XilaFileSystemSize = 0;
245        let mut entry_inode: XilaFileSystemInode = 0;
246        let context = get_context_instance();
247
248        // Test with invalid handle but valid output pointers
249        let result = context
250            .call_abi(async || unsafe {
251                xila_file_system_read_directory(
252                    invalid_handle,
253                    &mut entry_name,
254                    &mut entry_type,
255                    &mut entry_size,
256                    &mut entry_inode,
257                )
258            })
259            .await;
260        assert_ne!(result, 0, "Reading from invalid handle should fail");
261    }
262
263    #[test]
264    async fn test_set_position_boundary_values() {
265        initialize_test_environment().await; // Ensure the test environment is initialized
266        // Test set position with boundary values
267        let invalid_handle: XilaUniqueFileIdentifier = 999999;
268        let context = get_context_instance();
269
270        // Test with maximum value
271        let result = context
272            .call_abi(|| async {
273                xila_file_system_directory_set_position(invalid_handle, u64::MAX)
274            })
275            .await;
276        assert_ne!(
277            result, 0,
278            "Setting position with max value on invalid handle should fail"
279        );
280
281        // Test with zero value
282        let result = context
283            .call_abi(|| async { xila_file_system_directory_set_position(invalid_handle, 0) })
284            .await;
285        assert_ne!(
286            result, 0,
287            "Setting position with zero on invalid handle should fail"
288        );
289    }
290
291    #[test]
292    async fn test_open_directory_valid_path() {
293        let (_task, _vfs) = initialize_test_environment().await;
294        let context = get_context_instance();
295
296        let path = CString::new("/").unwrap();
297        let mut directory_id: XilaUniqueFileIdentifier = 0;
298
299        let result = context
300            .call_abi(async || unsafe {
301                xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
302            })
303            .await;
304        assert_eq!(result, 0, "Opening root directory should succeed");
305        assert_ne!(directory_id, 0, "Directory identifier should be non-zero");
306
307        // Clean up
308        let close_result = context
309            .call_abi(|| async { xila_file_system_close_directory(directory_id) })
310            .await;
311        assert_eq!(close_result, 0, "Closing directory should succeed");
312    }
313
314    #[test]
315    async fn test_open_directory_invalid_path() {
316        let (_task, _vfs) = initialize_test_environment().await;
317        let context = get_context_instance();
318
319        let path = CString::new("/nonexistent").unwrap();
320        let mut directory_id: XilaUniqueFileIdentifier = 0;
321
322        let result = context
323            .call_abi(async || unsafe {
324                xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
325            })
326            .await;
327
328        assert_ne!(result, 0, "Opening nonexistent directory should fail");
329    }
330
331    #[test]
332    async fn test_open_directory_null_path() {
333        let (_task, _vfs) = initialize_test_environment().await;
334        let context = get_context_instance();
335
336        let mut directory_id: XilaUniqueFileIdentifier = 0;
337
338        let result = context
339            .call_abi(async || unsafe {
340                xila_file_system_open_directory(core::ptr::null(), &mut directory_id)
341            })
342            .await;
343
344        assert_ne!(result, 0, "Opening directory with null path should fail");
345    }
346
347    #[test]
348    async fn test_read_directory_entries() {
349        let (_task, vfs) = initialize_test_environment().await;
350        let task = _task;
351        let context = get_context_instance();
352
353        // Create some test files and directories
354        vfs.create_directory(&"/test_read_directory_entries", task)
355            .await
356            .unwrap();
357
358        let test_file = vfs
359            .open(
360                &"/test_read_directory_entries.txt",
361                file_system::Flags::new(Mode::WRITE_ONLY, Some(Open::CREATE_ONLY), None),
362                task,
363            )
364            .await
365            .unwrap();
366        vfs.close(test_file, task).await.unwrap();
367
368        // Open root directory
369        let path = CString::new("/").unwrap();
370        let mut directory_id: XilaUniqueFileIdentifier = 0;
371
372        let open_result = context
373            .call_abi(async || unsafe {
374                xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
375            })
376            .await;
377        assert_eq!(open_result, 0, "Opening root directory should succeed");
378
379        // Read directory entries
380
381        let mut entries_found = Vec::new();
382
383        // Read all entries
384        loop {
385            let mut entry_name: *const c_char = core::ptr::null();
386            let mut entry_type: XilaFileKind = XilaFileKind::File;
387            let mut entry_size: XilaFileSystemSize = 0;
388            let mut entry_inode: XilaFileSystemInode = 0;
389
390            let read_result = context
391                .call_abi(async || unsafe {
392                    xila_file_system_read_directory(
393                        directory_id,
394                        &mut entry_name,
395                        &mut entry_type,
396                        &mut entry_size,
397                        &mut entry_inode,
398                    )
399                })
400                .await;
401
402            assert_eq!(read_result, 0, "Reading directory entry should succeed");
403
404            if entry_name.is_null() {
405                break; // End of directory
406            }
407
408            let name = unsafe { CStr::from_ptr(entry_name).to_string_lossy().into_owned() };
409            entries_found.push((name, entry_type));
410
411            // Free the allocated string
412            unsafe {
413                let _ = CString::from_raw(entry_name as *mut c_char);
414            }
415        }
416
417        // Verify we found the expected entries
418        assert!(
419            entries_found.len() >= 2,
420            "Should find at least current and parent directories"
421        );
422
423        // Check for current and parent directory entries
424        let has_current = entries_found.iter().any(|(name, _)| name == ".");
425        let has_parent = entries_found.iter().any(|(name, _)| name == "..");
426
427        assert!(has_current, "Should find current directory entry");
428        assert!(has_parent, "Should find parent directory entry");
429
430        // Clean up
431        let close_result = context
432            .call_abi(|| async { xila_file_system_close_directory(directory_id) })
433            .await;
434        assert_eq!(close_result, 0, "Closing directory should succeed");
435    }
436
437    #[test]
438    async fn test_read_directory_invalid_handle() {
439        let (_task, _vfs) = initialize_test_environment().await;
440        let context = get_context_instance();
441
442        let invalid_directory_id: XilaUniqueFileIdentifier = 999999;
443        let mut entry_name: *const c_char = core::ptr::null();
444        let mut entry_type: XilaFileKind = XilaFileKind::File;
445        let mut entry_size: XilaFileSystemSize = 0;
446        let mut entry_inode: XilaFileSystemInode = 0;
447
448        let result = context
449            .call_abi(async || unsafe {
450                xila_file_system_read_directory(
451                    invalid_directory_id,
452                    &mut entry_name,
453                    &mut entry_type,
454                    &mut entry_size,
455                    &mut entry_inode,
456                )
457            })
458            .await;
459
460        assert_ne!(
461            result, 0,
462            "Reading from invalid directory handle should fail"
463        );
464    }
465
466    #[test]
467    async fn test_close_directory_valid_handle() {
468        let (_task, _vfs) = initialize_test_environment().await;
469        let context = get_context_instance();
470
471        let path = CString::new("/").unwrap();
472        let mut directory_id: XilaUniqueFileIdentifier = 0;
473
474        // Open directory
475        let open_result = context
476            .call_abi(async || unsafe {
477                xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
478            })
479            .await;
480        assert_eq!(open_result, 0, "Opening directory should succeed");
481
482        // Close directory
483        let close_result = context
484            .call_abi(|| async { xila_file_system_close_directory(directory_id) })
485            .await;
486        assert_eq!(close_result, 0, "Closing directory should succeed");
487    }
488
489    #[test]
490    async fn test_close_directory_invalid_handle() {
491        let (_task, _vfs) = initialize_test_environment().await;
492        let context = get_context_instance();
493
494        let invalid_directory_id: XilaUniqueFileIdentifier = 999999;
495
496        let result = context
497            .call_abi(|| async { xila_file_system_close_directory(invalid_directory_id) })
498            .await;
499        assert_ne!(result, 0, "Closing invalid directory handle should fail");
500    }
501
502    #[test]
503    async fn test_rewind_directory() {
504        let (_task, vfs) = initialize_test_environment().await;
505        let task = _task;
506        let context = get_context_instance();
507
508        // Create some test files
509        for i in 0..3 {
510            let path = format!("/test_rewind_directory_{i}.txt");
511
512            let test_file = vfs
513                .open(
514                    &PathOwned::new(path).unwrap(),
515                    file_system::Flags::new(Mode::WRITE_ONLY, Some(Open::CREATE_ONLY), None),
516                    task,
517                )
518                .await
519                .unwrap();
520            vfs.close(test_file, task).await.unwrap();
521        }
522
523        // Open directory
524        let path = CString::new("/").unwrap();
525        let mut directory_id: XilaUniqueFileIdentifier = 0;
526
527        let open_result = context
528            .call_abi(async || unsafe {
529                xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
530            })
531            .await;
532        assert_eq!(open_result, 0, "Opening directory should succeed");
533
534        // Read a few entries
535        for _ in 0..2 {
536            let mut entry_name: *const c_char = core::ptr::null();
537            let mut entry_type: XilaFileKind = XilaFileKind::File;
538            let mut entry_size: XilaFileSystemSize = 0;
539            let mut entry_inode: XilaFileSystemInode = 0;
540
541            let read_result = context
542                .call_abi(async || unsafe {
543                    xila_file_system_read_directory(
544                        directory_id,
545                        &mut entry_name,
546                        &mut entry_type,
547                        &mut entry_size,
548                        &mut entry_inode,
549                    )
550                })
551                .await;
552            assert_eq!(read_result, 0, "Reading directory should succeed");
553
554            if !entry_name.is_null() {
555                unsafe {
556                    let _ = CString::from_raw(entry_name as *mut c_char);
557                }
558            }
559        }
560
561        // Rewind directory
562        let rewind_result = context
563            .call_abi(|| async { xila_file_system_rewind_directory(directory_id) })
564            .await;
565        assert_eq!(rewind_result, 0, "Rewinding directory should succeed");
566
567        // Read first entry again - should be "."
568        let mut entry_name: *const c_char = core::ptr::null();
569        let mut entry_type: XilaFileKind = XilaFileKind::File;
570        let mut entry_size: XilaFileSystemSize = 0;
571        let mut entry_inode: XilaFileSystemInode = 0;
572
573        let read_result = context
574            .call_abi(async || unsafe {
575                xila_file_system_read_directory(
576                    directory_id,
577                    &mut entry_name,
578                    &mut entry_type,
579                    &mut entry_size,
580                    &mut entry_inode,
581                )
582            })
583            .await;
584        assert_eq!(
585            read_result, 0,
586            "Reading directory after rewind should succeed"
587        );
588        assert!(!entry_name.is_null(), "Entry name should not be null");
589
590        let name = unsafe { CStr::from_ptr(entry_name).to_string_lossy() };
591        assert_eq!(
592            name, ".",
593            "First entry after rewind should be current directory"
594        );
595
596        unsafe {
597            let _ = CString::from_raw(entry_name as *mut c_char);
598        }
599
600        // Clean up
601        let close_result = context
602            .call_abi(|| async { xila_file_system_close_directory(directory_id) })
603            .await;
604        assert_eq!(close_result, 0, "Closing directory should succeed");
605    }
606
607    #[test]
608    async fn test_rewind_directory_invalid_handle() {
609        let (_task, _vfs) = initialize_test_environment().await;
610        let context = get_context_instance();
611
612        let invalid_directory_id: XilaUniqueFileIdentifier = 999999;
613
614        let result = context
615            .call_abi(|| async { xila_file_system_rewind_directory(invalid_directory_id) })
616            .await;
617        assert_ne!(result, 0, "Rewinding invalid directory handle should fail");
618    }
619
620    #[test]
621    async fn test_directory_set_position() {
622        let (_task, vfs) = initialize_test_environment().await;
623        let task = _task;
624        let context = get_context_instance();
625
626        // Create some test files
627        for i in 0..5 {
628            let path = format!("/test_file_{i}.txt");
629
630            let test_file = vfs
631                .open(
632                    &PathOwned::new(path).unwrap(),
633                    file_system::Flags::new(Mode::WRITE_ONLY, Some(Open::CREATE_ONLY), None),
634                    task,
635                )
636                .await
637                .unwrap();
638            vfs.close(test_file, task).await.unwrap();
639        }
640
641        // Open directory
642        let path = CString::new("/").unwrap();
643        let mut directory_id: XilaUniqueFileIdentifier = 0;
644
645        let open_result = context
646            .call_abi(async || unsafe {
647                xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
648            })
649            .await;
650        assert_eq!(open_result, 0, "Opening directory should succeed");
651
652        // Read a few entries to advance position
653        for _ in 0..3 {
654            let mut entry_name: *const c_char = core::ptr::null();
655            let mut entry_type: XilaFileKind = XilaFileKind::File;
656            let mut entry_size: XilaFileSystemSize = 0;
657            let mut entry_inode: XilaFileSystemInode = 0;
658
659            let read_result = context
660                .call_abi(async || unsafe {
661                    xila_file_system_read_directory(
662                        directory_id,
663                        &mut entry_name,
664                        &mut entry_type,
665                        &mut entry_size,
666                        &mut entry_inode,
667                    )
668                })
669                .await;
670            assert_eq!(read_result, 0, "Reading directory should succeed");
671
672            if !entry_name.is_null() {
673                unsafe {
674                    let _ = CString::from_raw(entry_name as *mut c_char);
675                }
676            }
677        }
678
679        // Set position back to start
680        let set_position_result = context
681            .call_abi(|| async { xila_file_system_directory_set_position(directory_id, 0) })
682            .await;
683        assert_eq!(
684            set_position_result, 0,
685            "Setting directory position should succeed"
686        );
687
688        // Read first entry - should be "." again
689        let mut entry_name: *const c_char = core::ptr::null();
690        let mut entry_type: XilaFileKind = XilaFileKind::File;
691        let mut entry_size: XilaFileSystemSize = 0;
692        let mut entry_inode: XilaFileSystemInode = 0;
693
694        let read_result = context
695            .call_abi(async || unsafe {
696                xila_file_system_read_directory(
697                    directory_id,
698                    &mut entry_name,
699                    &mut entry_type,
700                    &mut entry_size,
701                    &mut entry_inode,
702                )
703            })
704            .await;
705        assert_eq!(
706            read_result, 0,
707            "Reading directory after position reset should succeed"
708        );
709        assert!(!entry_name.is_null(), "Entry name should not be null");
710
711        let name = unsafe { CStr::from_ptr(entry_name).to_string_lossy() };
712        assert_eq!(
713            name, ".",
714            "First entry after position reset should be current directory"
715        );
716
717        unsafe {
718            let _ = CString::from_raw(entry_name as *mut c_char);
719        }
720
721        // Clean up
722        let close_result = context
723            .call_abi(|| async { xila_file_system_close_directory(directory_id) })
724            .await;
725        assert_eq!(close_result, 0, "Closing directory should succeed");
726    }
727
728    #[test]
729    async fn test_directory_set_position_invalid_handle() {
730        let (_task, _vfs) = initialize_test_environment().await;
731        let context = get_context_instance();
732
733        let invalid_directory_id: XilaUniqueFileIdentifier = 999999;
734
735        let result = context
736            .call_abi(|| async { xila_file_system_directory_set_position(invalid_directory_id, 0) })
737            .await;
738        assert_ne!(
739            result, 0,
740            "Setting position on invalid directory handle should fail"
741        );
742    }
743
744    #[test]
745    async fn test_directory_operations_sequence() {
746        let (_task, vfs) = initialize_test_environment().await;
747        let task = _task;
748        let context = get_context_instance();
749
750        // Create test structure
751        vfs.create_directory(&"/test_dir", task).await.unwrap();
752        vfs.create_directory(&"/test_dir/subdir", task)
753            .await
754            .unwrap();
755
756        let test_file = vfs
757            .open(
758                &"/test_dir/file.txt",
759                file_system::Flags::new(Mode::WRITE_ONLY, Some(Open::CREATE_ONLY), None),
760                task,
761            )
762            .await
763            .unwrap();
764        vfs.close(test_file, task).await.unwrap();
765
766        // Test opening the created directory
767        let path = CString::new("/test_dir").unwrap();
768        let mut directory_id: XilaUniqueFileIdentifier = 0;
769
770        let open_result = context
771            .call_abi(async || unsafe {
772                xila_file_system_open_directory(path.as_ptr(), &mut directory_id)
773            })
774            .await;
775        assert_eq!(open_result, 0, "Opening test directory should succeed");
776
777        // Count entries
778        let mut entry_count = 0;
779        loop {
780            let mut entry_name: *const c_char = core::ptr::null();
781            let mut entry_type: XilaFileKind = XilaFileKind::File;
782            let mut entry_size: XilaFileSystemSize = 0;
783            let mut entry_inode: XilaFileSystemInode = 0;
784
785            let read_result = context
786                .call_abi(async || unsafe {
787                    xila_file_system_read_directory(
788                        directory_id,
789                        &mut entry_name,
790                        &mut entry_type,
791                        &mut entry_size,
792                        &mut entry_inode,
793                    )
794                })
795                .await;
796            assert_eq!(read_result, 0, "Reading directory should succeed");
797
798            if entry_name.is_null() {
799                break;
800            }
801
802            entry_count += 1;
803            unsafe {
804                let _ = CString::from_raw(entry_name as *mut c_char);
805            }
806        }
807
808        // Should have at least: ., .., subdir, file.txt
809        assert!(
810            entry_count >= 4,
811            "Should find at least 4 entries in test directory"
812        );
813
814        // Test rewind and count again
815        let rewind_result = context
816            .call_abi(|| async { xila_file_system_rewind_directory(directory_id) })
817            .await;
818        assert_eq!(rewind_result, 0, "Rewinding directory should succeed");
819
820        let mut rewind_count = 0;
821        loop {
822            let mut entry_name: *const c_char = core::ptr::null();
823            let mut entry_type: XilaFileKind = XilaFileKind::File;
824            let mut entry_size: XilaFileSystemSize = 0;
825            let mut entry_inode: XilaFileSystemInode = 0;
826
827            let read_result = context
828                .call_abi(async || unsafe {
829                    xila_file_system_read_directory(
830                        directory_id,
831                        &mut entry_name,
832                        &mut entry_type,
833                        &mut entry_size,
834                        &mut entry_inode,
835                    )
836                })
837                .await;
838            assert_eq!(
839                read_result, 0,
840                "Reading directory after rewind should succeed"
841            );
842
843            if entry_name.is_null() {
844                break;
845            }
846
847            rewind_count += 1;
848            unsafe {
849                let _ = CString::from_raw(entry_name as *mut c_char);
850            }
851        }
852
853        assert_eq!(
854            entry_count, rewind_count,
855            "Entry count should be same after rewind"
856        );
857
858        // Clean up
859        let close_result = context
860            .call_abi(|| async { xila_file_system_close_directory(directory_id) })
861            .await;
862        assert_eq!(close_result, 0, "Closing directory should succeed");
863    }
864
865    #[test]
866    async fn test_directory_operations_error_handling() {
867        initialize_test_environment().await;
868
869        let context = get_context_instance();
870
871        // Test null pointer handling
872        let mut directory_id: XilaUniqueFileIdentifier = 0;
873
874        // Test with null path
875        let result = context
876            .call_abi(async || unsafe {
877                xila_file_system_open_directory(core::ptr::null(), &mut directory_id)
878            })
879            .await;
880        assert_ne!(result, 0, "Null path should cause error");
881
882        // Test invalid operations on invalid handles
883        let invalid_handle = 0usize;
884
885        let close_result = context
886            .call_abi(|| async { xila_file_system_close_directory(invalid_handle) })
887            .await;
888        assert_ne!(close_result, 0, "Invalid close should fail");
889
890        let rewind_result = context
891            .call_abi(|| async { xila_file_system_rewind_directory(invalid_handle) })
892            .await;
893        assert_ne!(rewind_result, 0, "Invalid rewind should fail");
894
895        let set_pos_result = context
896            .call_abi(|| async { xila_file_system_directory_set_position(invalid_handle, 0) })
897            .await;
898        assert_ne!(set_pos_result, 0, "Invalid set position should fail");
899
900        let mut entry_name: *const c_char = core::ptr::null();
901        let mut entry_type: XilaFileKind = XilaFileKind::File;
902        let mut entry_size: XilaFileSystemSize = 0;
903        let mut entry_inode: XilaFileSystemInode = 0;
904
905        let read_result = context
906            .call_abi(async || unsafe {
907                xila_file_system_read_directory(
908                    invalid_handle,
909                    &mut entry_name,
910                    &mut entry_type,
911                    &mut entry_size,
912                    &mut entry_inode,
913                )
914            })
915            .await;
916        assert_ne!(read_result, 0, "Invalid read should fail");
917    }
918}