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 {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
24impl Entry {
25    /// Returns the file name of this entry
26    pub fn file_name(&self) -> &OsStr {
27        OsStr::from_bytes(self.name.to_bytes())
28    }
29    /// Returns the simplified type of this entry
30    pub fn simple_type(&self) -> Option<SimpleType> {
31        self.file_type
32    }
33}
34
35#[cfg(any(target_os="linux", target_os="fuchsia"))]
36unsafe fn errno_location() -> *mut libc::c_int {
37    libc::__errno_location()
38}
39
40#[cfg(any(target_os="openbsd", target_os="netbsd", target_os="android"))]
41unsafe fn errno_location() -> *mut libc::c_int {
42    libc::__errno()
43}
44
45#[cfg(not(any(target_os="linux", target_os="openbsd", target_os="netbsd", target_os="android", target_os="fuchsia")))]
46unsafe fn errno_location() -> *mut libc::c_int {
47    libc::__error()
48}
49
50impl DirIter {
51
52    unsafe fn next_entry(&mut self) -> io::Result<Option<*const libc::dirent>>
53    {
54        // Reset errno to detect if error occurred
55        *errno_location() = 0;
56
57        let entry = libc::readdir(self.dir);
58        if entry == ptr::null_mut() {
59            if *errno_location() == 0 {
60                return Ok(None)
61            } else {
62                return Err(io::Error::last_os_error());
63            }
64        }
65        return Ok(Some(entry));
66    }
67}
68
69pub fn open_dir(dir: &Dir, path: &CStr) -> io::Result<DirIter> {
70    let dir_fd = unsafe {
71        libc::openat(dir.0, path.as_ptr(), libc::O_DIRECTORY|libc::O_CLOEXEC)
72    };
73    if dir_fd < 0 {
74        Err(io::Error::last_os_error())
75    } else {
76        let dir = unsafe { libc::fdopendir(dir_fd) };
77        if dir == ptr::null_mut() {
78            Err(io::Error::last_os_error())
79        } else {
80            Ok(DirIter { dir: dir })
81        }
82    }
83}
84
85impl Iterator for DirIter {
86    type Item = io::Result<Entry>;
87    fn next(&mut self) -> Option<Self::Item> {
88        unsafe {
89            loop {
90                match self.next_entry() {
91                    Err(e) => return Some(Err(e)),
92                    Ok(None) => return None,
93                    Ok(Some(e)) if (*e).d_name[..2] == DOT => continue,
94                    Ok(Some(e)) if (*e).d_name[..3] == DOTDOT => continue,
95                    Ok(Some(e)) => {
96                        return Some(Ok(Entry {
97                            name: CStr::from_ptr((&(*e).d_name).as_ptr())
98                                .to_owned(),
99                            file_type: match (*e).d_type {
100                                0 => None,
101                                libc::DT_REG => Some(SimpleType::File),
102                                libc::DT_DIR => Some(SimpleType::Dir),
103                                libc::DT_LNK => Some(SimpleType::Symlink),
104                                _ => Some(SimpleType::Other),
105                            },
106                        }));
107                    }
108                }
109            }
110        }
111    }
112}
113
114impl Drop for DirIter {
115    fn drop(&mut self) {
116        unsafe {
117            libc::closedir(self.dir);
118        }
119    }
120}