unix_fd/
dir.rs

1extern crate libc;
2extern crate errno;
3
4use std::io::Error;
5use std::fmt;
6use std::ffi::{CStr, OsString, OsStr};
7use std::os::unix::ffi::OsStrExt;
8
9use crate::fd::Fd;
10use crate::errors::*;
11
12extern {
13    // this is missing in libc crate :(
14    pub fn fdopendir(fd: libc::c_int) -> *mut libc::DIR;
15}
16
17// wrap a DIR stream and destroy it automatically
18#[derive(Debug)]
19pub struct Dir {
20    dirp: *mut libc::DIR,
21}
22
23impl Drop for Dir {
24    fn drop(&mut self)
25    {
26        let rc = unsafe { libc::closedir(self.dirp) };
27
28        if rc < 0 {
29            warn!("closedir({:?}) failed in drop(): {:?}", self,
30                  Error::last_os_error());
31        }
32    }
33}
34
35impl Dir {
36    pub fn fdopendir(fd: &Fd) -> Result<Dir>
37    {
38	#[allow(unused_parens)]
39        const FLAGS: libc::c_int = (libc::O_DIRECTORY | libc::O_CLOEXEC |
40                                    libc::O_RDONLY | libc::O_NOFOLLOW);
41
42        // do not use dupfd() here; fds share file offsets which is
43        // usually not wanted
44        let fd = fd.to_fdraw().openat(&OsString::from("."), FLAGS)?;
45
46        let dir = {
47            let dir = unsafe { fdopendir(fd.fd) };
48            ensure!(!dir.is_null(), Error::last_os_error());
49
50            fd.is_managed.set(false);
51
52            dir
53        };
54
55        Ok(Dir {
56            dirp: dir,
57        })
58    }
59
60    fn libc_readdir(&mut self) -> Result<*const libc::dirent>
61    {
62        errno::set_errno(errno::Errno(0));
63
64        let entryp = unsafe { libc::readdir(self.dirp) };
65
66        ensure!(!entryp.is_null() || errno::errno().0 == 0,
67                Error::last_os_error());
68
69        Ok(entryp)
70    }
71
72    pub fn readdir(self) -> ReadDir {
73        ReadDir::new(self)
74    }
75}
76
77#[derive(Clone)]
78pub struct DirEntry {
79    pub d_name:		OsString,
80    pub d_ino:		libc::c_ulong,
81    pub d_off:		libc::loff_t,
82    pub d_type:		u8,
83}
84
85impl DirEntry {
86    pub fn from_dirent(dirent: libc::dirent) -> Self {
87        let name_c = unsafe { CStr::from_ptr(dirent.d_name.as_ptr()) };
88        let name  = OsStr::from_bytes(name_c.to_bytes());
89
90	Self {
91	    d_name:	name.into(),
92	    d_ino:	dirent.d_ino,
93	    d_off:	dirent.d_off,
94	    d_type:	dirent.d_type,
95	}
96    }
97
98    pub fn name(&self) -> &OsStr {
99	&self.d_name
100    }
101}
102
103impl fmt::Debug for DirEntry {
104    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105        write!(f,
106               "dirent {{ ino={:?}, off={:?}, type={:?}, name='{:?}' }}",
107               self.d_ino, self.d_off, self.d_type,
108               self.d_name)
109    }
110}
111
112pub struct ReadDir {
113    dir: Dir,
114    failed: bool,
115}
116
117impl ReadDir {
118    pub fn new(dir: Dir) -> ReadDir {
119        ReadDir {
120            dir: dir,
121            failed: false,
122        }
123    }
124}
125
126impl Iterator for ReadDir {
127    type Item = Result<DirEntry>;
128
129    fn next(&mut self) -> Option<Self::Item> {
130        loop {
131            let entryp = self.dir.libc_readdir();
132            let entry_raw = match entryp {
133                Err(e) => {
134                    if self.failed {
135                        break None;
136                    } else {
137                        self.failed = true;
138                        break Some(Err(e).chain_err(|| "readdir() failed"));
139                    }
140                }
141                Ok(e) => e,
142            };
143
144            if entry_raw.is_null() {
145                break None;
146            }
147
148            self.failed = false;
149
150            let entry = DirEntry::from_dirent(unsafe { *entry_raw });
151
152            match entry.name().as_bytes() {
153                // ignore '.' and '..' directory entries
154                b"." | b".." => {}
155                _ => break Some(Ok(entry)),
156            }
157        }
158    }
159}