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 pub fn fdopendir(fd: libc::c_int) -> *mut libc::DIR;
15}
16
17#[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 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 b"." | b".." => {}
155 _ => break Some(Ok(entry)),
156 }
157 }
158 }
159}