Skip to main content

ext4_lwext4/
dir.rs

1//! Directory operations for ext4 filesystems.
2
3use crate::error::{check_errno_with_path, Result};
4use crate::fs::Ext4Fs;
5use crate::types::FileType;
6use ext4_lwext4_sys::{ext4_dir, ext4_dir_close, ext4_dir_entry_next, ext4_dir_entry_rewind, ext4_dir_open};
7
8/// A directory handle for iterating over entries.
9///
10/// Directories are automatically closed when dropped.
11pub struct Dir<'a> {
12    #[allow(dead_code)]
13    fs: &'a Ext4Fs,
14    inner: ext4_dir,
15}
16
17impl<'a> Dir<'a> {
18    /// Open a directory for iteration.
19    pub(crate) fn open(fs: &'a Ext4Fs, path: &str) -> Result<Self> {
20        let full_path = fs.make_path(path)?;
21        let mut inner = ext4_dir::default();
22
23        let ret = unsafe { ext4_dir_open(&mut inner, full_path.as_ptr()) };
24        check_errno_with_path(ret, path)?;
25
26        Ok(Self { fs, inner })
27    }
28
29    /// Get the next directory entry.
30    pub fn next_entry(&mut self) -> Option<Result<DirEntry>> {
31        let entry_ptr = unsafe { ext4_dir_entry_next(&mut self.inner) };
32
33        if entry_ptr.is_null() {
34            return None;
35        }
36
37        let entry = unsafe { &*entry_ptr };
38
39        // Extract name (not null-terminated!)
40        let name_len = entry.name_length as usize;
41        let name_bytes = &entry.name[..name_len];
42        let name = match String::from_utf8(name_bytes.to_vec()) {
43            Ok(s) => s,
44            Err(_) => {
45                // Try lossy conversion for non-UTF8 names
46                String::from_utf8_lossy(name_bytes).into_owned()
47            }
48        };
49
50        Some(Ok(DirEntry {
51            name,
52            inode: entry.inode as u64,
53            file_type: FileType::from_raw(entry.inode_type),
54        }))
55    }
56
57    /// Rewind to the beginning of the directory.
58    pub fn rewind(&mut self) {
59        unsafe {
60            ext4_dir_entry_rewind(&mut self.inner);
61        }
62    }
63}
64
65impl Drop for Dir<'_> {
66    fn drop(&mut self) {
67        unsafe {
68            ext4_dir_close(&mut self.inner);
69        }
70    }
71}
72
73impl<'a> Iterator for Dir<'a> {
74    type Item = Result<DirEntry>;
75
76    fn next(&mut self) -> Option<Self::Item> {
77        self.next_entry()
78    }
79}
80
81/// A directory entry.
82#[derive(Debug, Clone)]
83pub struct DirEntry {
84    /// Entry name
85    pub name: String,
86    /// Inode number
87    pub inode: u64,
88    /// File type
89    pub file_type: FileType,
90}
91
92impl DirEntry {
93    /// Get the entry name.
94    pub fn name(&self) -> &str {
95        &self.name
96    }
97
98    /// Get the inode number.
99    pub fn inode(&self) -> u64 {
100        self.inode
101    }
102
103    /// Get the file type.
104    pub fn file_type(&self) -> FileType {
105        self.file_type
106    }
107
108    /// Check if this is a regular file.
109    pub fn is_file(&self) -> bool {
110        self.file_type.is_file()
111    }
112
113    /// Check if this is a directory.
114    pub fn is_dir(&self) -> bool {
115        self.file_type.is_dir()
116    }
117
118    /// Check if this is a symbolic link.
119    pub fn is_symlink(&self) -> bool {
120        self.file_type.is_symlink()
121    }
122}