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#[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 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 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 buf.set_len(buf_sz);
242 }
243
244 if buf_sz != buf.capacity() {
245 return Ok(OsString::from_vec(buf));
246 }
247
248 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}