unix_fd/
fd.rs

1extern crate errno;
2extern crate libc;
3
4use std;
5use std::cell::Cell;
6use std::io::Error;
7use std::path::Path;
8use std::ffi::OsString;
9use std::os::unix::ffi::OsStringExt;
10
11use std::mem;
12
13use crate::errors::*;
14
15use crate::LibcString;
16
17macro_rules! try_errno {
18    ($expr:expr) => {{
19        let rc = $expr;
20
21        ensure!(rc >= 0, Error::last_os_error());
22
23        rc
24    }}
25}
26
27#[allow(non_camel_case_types)]
28type int = libc::c_int;
29
30// wrap a file descriptor and close it automatically
31#[derive(Debug)]
32pub struct FdRaw {
33    pub(crate) fd: libc::c_int,
34    pub(crate) is_managed: Cell<bool>,
35}
36
37impl Drop for FdRaw {
38    fn drop(&mut self) {
39        if self.is_managed.get() {
40            let rc = unsafe { libc::close(self.fd) };
41
42            if rc < 0 {
43                warn!("close({:?}) failed in drop(): {:?}", self,
44                      Error::last_os_error());
45            }
46        }
47    }
48}
49
50impl FdRaw {
51    fn _new(fd: int) -> Self {
52        Self {
53            fd: fd,
54            is_managed: Cell::new(fd >= 0 && fd != libc::AT_FDCWD),
55        }
56    }
57
58    fn _new_unmanaged(fd: int) -> Self {
59        Self {
60            fd: fd,
61            is_managed: Cell::new(false),
62        }
63    }
64
65    pub fn into_file(self) -> Result<std::fs::File> {
66        use std::os::unix::io::FromRawFd;
67
68        let res = unsafe { std::fs::File::from_raw_fd(self.fd) };
69        self.is_managed.set(false);
70
71        Ok(res)
72    }
73
74    pub fn open<T: AsRef<Path>>(path: &T, flags: int) -> Result<Self> {
75        let fd = try_errno!(unsafe {
76            libc::open(path.as_ref().as_libc().0, flags)
77        });
78
79        Ok(Self::_new(fd))
80    }
81
82    pub fn openat<T: AsRef<Path>>(&self, path: &T, flags: int) -> Result<Self> {
83        let fd = try_errno!(unsafe {
84            libc::openat(self.fd, path.as_ref().as_libc().0, flags)
85        });
86
87        Ok(Self::_new(fd))
88    }
89
90    pub fn createat<T: AsRef<Path>>(&self, path: &T, flags: int,
91                                    mode: u32) -> Result<Self>
92    {
93        let fd = try_errno!(unsafe {
94            libc::openat(self.fd, path.as_ref().as_libc().0,
95                         flags | libc::O_CREAT, mode)
96        });
97
98        Ok(Self::_new(fd))
99    }
100
101    pub fn mkdirat<T: AsRef<Path>>(&self, path: &T, mode: u32) -> Result<()> {
102        try_errno!(unsafe {
103            libc::mkdirat(self.fd, path.as_ref().as_libc().0, mode)
104        });
105
106        Ok(())
107    }
108
109    pub fn symlinkat<D,T>(&self, target: &D, path: &T) -> Result<()>
110    where
111        D: AsRef<Path>,
112        T: AsRef<Path>,
113    {
114        try_errno!(unsafe {
115            libc::symlinkat(target.as_ref().as_libc().0,
116                            self.fd,
117                            path.as_ref().as_libc().0)
118        });
119
120        Ok(())
121    }
122
123    pub unsafe fn new(fd: int) -> Self {
124        assert!(fd >= 0);
125
126        Self::_new(fd)
127    }
128
129    pub fn cwd() -> Self {
130        Self::_new(libc::AT_FDCWD)
131    }
132
133    pub unsafe fn as_unmanaged(&self) -> Self {
134        Self::_new_unmanaged(self.fd)
135    }
136
137    pub fn into_fd(self) -> Fd {
138        Fd::from_rawfd(self)
139    }
140
141    pub fn dupfd(&self, cloexec: bool) -> Result<Self> {
142        let cmd: int = match cloexec {
143            true	=> libc::F_DUPFD_CLOEXEC,
144            false	=> libc::F_DUPFD,
145        };
146
147        // start at fd 3 to avoid overriding some of the stdXXX
148        let min_fd: int = 3;
149
150        let fd = try_errno!(unsafe { libc::fcntl(self.fd, cmd, min_fd) });
151
152        Ok(Self::_new(fd))
153    }
154
155    fn is_file_type(&self, fname: &Path, file_type: u32) -> bool {
156        let stat = self.fstatat(&fname, false);
157        match stat {
158            Err(_) => false,
159            Ok(s) => (s.st_mode & libc::S_IFMT) == file_type,
160        }
161    }
162
163    pub fn is_lnkat<T: AsRef<Path>>(&self, fname: &T) -> bool {
164        self.is_file_type(fname.as_ref(), libc::S_IFLNK)
165    }
166
167    pub fn is_regat<T: AsRef<Path>>(&self, fname: &T) -> bool {
168        self.is_file_type(fname.as_ref(), libc::S_IFREG)
169    }
170
171    pub fn is_dirat<T: AsRef<Path>>(&self, fname: &T) -> bool {
172        self.is_file_type(fname.as_ref(), libc::S_IFDIR)
173    }
174
175    pub fn stat<T>(fname: &T, do_follow: bool) -> Result<libc::stat>
176    where
177        T: AsRef<Path>
178    {
179	#[allow(clippy::uninit_assumed_init)]
180        let mut stat: libc::stat = unsafe { mem::MaybeUninit::uninit().assume_init() };
181
182        try_errno!(unsafe {
183            if do_follow {
184                libc::stat(fname.as_ref().as_libc().0, &mut stat)
185            } else {
186                libc::lstat(fname.as_ref().as_libc().0, &mut stat)
187            }
188        });
189
190        Ok(stat)
191    }
192
193    pub fn fstatat<T>(&self, fname: &T, do_follow: bool) -> Result<libc::stat>
194    where
195        T: AsRef<Path>
196    {
197        let flags = if do_follow {
198            0
199        } else {
200            libc::AT_SYMLINK_NOFOLLOW
201        };
202
203	#[allow(clippy::uninit_assumed_init)]
204        let mut stat: libc::stat = unsafe { mem::MaybeUninit::uninit().assume_init() };
205
206        try_errno!(unsafe {
207            libc::fstatat(self.fd, fname.as_ref().as_libc().0, &mut stat, flags)
208        });
209
210        Ok(stat)
211    }
212
213    pub fn fstat(&self) -> Result<libc::stat> {
214	#[allow(clippy::uninit_assumed_init)]
215        let mut stat: libc::stat = unsafe { mem::MaybeUninit::uninit().assume_init() };
216
217        try_errno!(unsafe {
218            libc::fstat(self.fd, &mut stat)
219        });
220
221        Ok(stat)
222    }
223
224    pub fn readlinkat<T: AsRef<Path>>(&self, fname: &T) -> Result<OsString> {
225        let mut buf = Vec::with_capacity(256);
226
227        loop {
228            let buf_sz = try_errno!(unsafe {
229                // on overflow, readlinkat() returns buf.capacity();
230                // else the number of actually written bytes
231                libc::readlinkat(self.fd, fname.as_ref().as_libc().0,
232                                 buf.as_mut_ptr() as *mut _,
233                                 buf.capacity())
234            }) as usize;
235
236            assert!(buf_sz <= buf.capacity());
237
238            unsafe {
239                // set size; because readlinkat() returns <= capacity(),
240                // this can be done directly.
241                buf.set_len(buf_sz);
242            }
243
244            if buf_sz != buf.capacity() {
245                return Ok(OsString::from_vec(buf));
246            }
247
248            // readlinkat() overflowed; reserve additional space and
249            // try again...
250            buf.reserve(256);
251        }
252    }
253}
254
255#[cfg(not(feature = "atomic-rc"))]
256type Rc<T> = std::rc::Rc<T>;
257
258#[cfg(feature = "atomic-rc")]
259type Rc<T> = std::sync::Arc<T>;
260
261#[derive(Clone, Debug)]
262pub struct Fd(Rc<FdRaw>);
263
264impl Fd {
265    pub fn from_rawfd(fd: FdRaw) -> Self {
266        Fd(Rc::new(fd))
267    }
268
269    pub fn to_fdraw(&self) -> &FdRaw {
270        &self.0
271    }
272
273    pub fn open<T: AsRef<Path>>(path: &T, flags: int) -> Result<Self> {
274        FdRaw::open(path, flags).map(Self::from_rawfd)
275    }
276
277    pub fn openat<T: AsRef<Path>>(&self, path: &T, flags: int) -> Result<Self> {
278        self.0.openat(path, flags).map(Self::from_rawfd)
279    }
280
281    pub fn createat<T: AsRef<Path>>(&self, path: &T, flags:
282                                    int, mode: u32) -> Result<Self> {
283        self.0.createat(path, flags, mode).map(Self::from_rawfd)
284    }
285
286    pub fn cwd() -> Self {
287        Self::from_rawfd(FdRaw::cwd())
288    }
289
290    pub fn into_rawfd(self) -> std::result::Result<FdRaw, Fd> {
291        match Rc::try_unwrap(self.0) {
292            Err(fd) => Err(Fd(fd)),
293            Ok(fd) => Ok(fd),
294        }
295    }
296
297    pub unsafe fn into_file(self) -> Result<std::fs::File>
298    {
299        self.into_rawfd().unwrap().into_file()
300    }
301}
302
303impl std::ops::Deref for Fd {
304    type Target = FdRaw;
305
306    fn deref(&self) -> &Self::Target {
307        &self.0
308    }
309}
310
311pub fn same_file_by_stat(a: &libc::stat, b: &libc::stat) -> bool {
312    a.st_dev == b.st_dev && a.st_ino == b.st_ino && a.st_mode == b.st_mode
313}