openat/
list.rs

1use std::io;
2use std::ptr;
3use std::ffi::{CStr, OsStr};
4use std::os::unix::ffi::OsStrExt;
5
6use libc;
7
8use crate::{Dir, Entry, SimpleType};
9
10
11// We have such weird constants because C types are ugly
12const DOT: [libc::c_char; 2] = [b'.' as libc::c_char, 0];
13const DOTDOT: [libc::c_char; 3] = [b'.' as libc::c_char, b'.' as libc::c_char, 0];
14
15
16/// Iterator over directory entries
17///
18/// Created using `Dir::list_dir()`
19#[derive(Debug)]
20pub struct DirIter {
21    dir: *mut libc::DIR,
22}
23
24/// Position in a DirIter as obtained by 'DirIter::current_position()'
25///
26/// The position is only valid for the DirIter it was retrieved from.
27pub struct DirPosition {
28    pos: libc::c_long,
29}
30
31impl Entry {
32    /// Returns the file name of this entry
33    pub fn file_name(&self) -> &OsStr {
34        OsStr::from_bytes(self.name.to_bytes())
35    }
36    /// Returns the simplified type of this entry
37    pub fn simple_type(&self) -> Option<SimpleType> {
38        self.file_type
39    }
40}
41
42#[cfg(any(target_os="linux", target_os="fuchsia"))]
43unsafe fn errno_location() -> *mut libc::c_int {
44    libc::__errno_location()
45}
46
47#[cfg(any(target_os="openbsd", target_os="netbsd", target_os="android"))]
48unsafe fn errno_location() -> *mut libc::c_int {
49    libc::__errno()
50}
51
52#[cfg(not(any(target_os="linux", target_os="openbsd", target_os="netbsd", target_os="android", target_os="fuchsia")))]
53unsafe fn errno_location() -> *mut libc::c_int {
54    libc::__error()
55}
56
57impl DirIter {
58
59    unsafe fn next_entry(&mut self) -> io::Result<Option<&libc::dirent>>
60    {
61        // Reset errno to detect if error occurred
62        *errno_location() = 0;
63
64        let entry = libc::readdir(self.dir);
65        if entry == ptr::null_mut() {
66            if *errno_location() == 0 {
67                return Ok(None)
68            } else {
69                return Err(io::Error::last_os_error());
70            }
71        }
72        return Ok(Some(&*entry));
73    }
74
75    /// Returns the current directory iterator position. The result should be handled as opaque value
76    pub fn current_position(&self) -> io::Result<DirPosition> {
77        let pos = unsafe { libc::telldir(self.dir) };
78
79        if pos == -1 {
80            Err(io::Error::last_os_error())
81        } else {
82            Ok(DirPosition { pos })
83        }
84    }
85
86    // note the C-API does not report errors for seekdir/rewinddir, thus we don't do as well.
87    /// Sets the current directory iterator position to some location queried by 'current_position()'
88    pub fn seek(&self, position: DirPosition) {
89        unsafe { libc::seekdir(self.dir, position.pos) };
90    }
91
92    /// Resets the current directory iterator position to the beginning
93    pub fn rewind(&self) {
94        unsafe { libc::rewinddir(self.dir) };
95    }
96}
97
98pub fn open_dirfd(fd: libc::c_int) -> io::Result<DirIter> {
99    let dir = unsafe { libc::fdopendir(fd) };
100    if dir == std::ptr::null_mut() {
101        Err(io::Error::last_os_error())
102    } else {
103        Ok(DirIter { dir: dir })
104    }
105}
106
107pub fn open_dir(dir: &Dir, path: &CStr) -> io::Result<DirIter> {
108    let dir_fd = unsafe {
109        libc::openat(dir.0, path.as_ptr(), libc::O_DIRECTORY|libc::O_CLOEXEC)
110    };
111    if dir_fd < 0 {
112        Err(io::Error::last_os_error())
113    } else {
114        open_dirfd(dir_fd)
115    }
116}
117
118impl Iterator for DirIter {
119    type Item = io::Result<Entry>;
120    fn next(&mut self) -> Option<Self::Item> {
121        unsafe {
122            loop {
123                match self.next_entry() {
124                    Err(e) => return Some(Err(e)),
125                    Ok(None) => return None,
126                    Ok(Some(e)) if e.d_name[..2] == DOT => continue,
127                    Ok(Some(e)) if e.d_name[..3] == DOTDOT => continue,
128                    Ok(Some(e)) => {
129                        return Some(Ok(Entry {
130                            name: CStr::from_ptr((e.d_name).as_ptr())
131                                .to_owned(),
132                            file_type: match e.d_type {
133                                0 => None,
134                                libc::DT_REG => Some(SimpleType::File),
135                                libc::DT_DIR => Some(SimpleType::Dir),
136                                libc::DT_LNK => Some(SimpleType::Symlink),
137                                _ => Some(SimpleType::Other),
138                            },
139                        }));
140                    }
141                }
142            }
143        }
144    }
145}
146
147impl Drop for DirIter {
148    fn drop(&mut self) {
149        unsafe {
150            libc::closedir(self.dir);
151        }
152    }
153}