]>
Commit | Line | Data |
---|---|---|
532ac7d7 XL |
1 | use crate::os::unix::prelude::*; |
2 | ||
dfeec247 | 3 | use crate::ffi::{CStr, CString, OsStr, OsString}; |
532ac7d7 | 4 | use crate::fmt; |
cdc7bbd5 | 5 | use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom}; |
532ac7d7 | 6 | use crate::mem; |
94222f64 | 7 | use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; |
532ac7d7 XL |
8 | use crate::path::{Path, PathBuf}; |
9 | use crate::ptr; | |
10 | use crate::sync::Arc; | |
11 | use crate::sys::fd::FileDesc; | |
12 | use crate::sys::time::SystemTime; | |
13 | use crate::sys::{cvt, cvt_r}; | |
94222f64 | 14 | use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; |
532ac7d7 | 15 | |
136023e0 XL |
16 | #[cfg(any( |
17 | all(target_os = "linux", target_env = "gnu"), | |
18 | target_os = "macos", | |
19 | target_os = "ios", | |
20 | ))] | |
21 | use crate::sys::weak::syscall; | |
22 | #[cfg(target_os = "macos")] | |
23 | use crate::sys::weak::weak; | |
24 | ||
532ac7d7 | 25 | use libc::{c_int, mode_t}; |
7453a54e | 26 | |
136023e0 XL |
27 | #[cfg(any( |
28 | target_os = "macos", | |
29 | target_os = "ios", | |
30 | all(target_os = "linux", target_env = "gnu") | |
31 | ))] | |
32 | use libc::c_char; | |
94b46f34 XL |
33 | #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] |
34 | use libc::dirfd; | |
dfeec247 XL |
35 | #[cfg(any(target_os = "linux", target_os = "emscripten"))] |
36 | use libc::fstatat64; | |
37 | #[cfg(not(any( | |
38 | target_os = "linux", | |
39 | target_os = "emscripten", | |
40 | target_os = "solaris", | |
ba9703b0 | 41 | target_os = "illumos", |
dfeec247 XL |
42 | target_os = "l4re", |
43 | target_os = "fuchsia", | |
44 | target_os = "redox" | |
45 | )))] | |
46 | use libc::readdir_r as readdir64_r; | |
7453a54e | 47 | #[cfg(target_os = "android")] |
dfeec247 XL |
48 | use libc::{ |
49 | dirent as dirent64, fstat as fstat64, fstatat as fstatat64, lseek64, lstat as lstat64, | |
50 | open as open64, stat as stat64, | |
51 | }; | |
52 | #[cfg(not(any( | |
53 | target_os = "linux", | |
54 | target_os = "emscripten", | |
55 | target_os = "l4re", | |
56 | target_os = "android" | |
57 | )))] | |
58 | use libc::{ | |
59 | dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, | |
60 | lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, | |
61 | }; | |
62 | #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))] | |
63 | use libc::{ | |
64 | dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64, | |
65 | }; | |
85aaf69f | 66 | |
17df50a5 | 67 | pub use crate::sys_common::fs::{remove_dir_all, try_exists}; |
532ac7d7 | 68 | |
85aaf69f SL |
69 | pub struct File(FileDesc); |
70 | ||
dfeec247 XL |
71 | // FIXME: This should be available on Linux with all `target_env`. |
72 | // But currently only glibc exposes `statx` fn and structs. | |
73 | // We don't want to import unverified raw C structs here directly. | |
74 | // https://github.com/rust-lang/rust/pull/67774 | |
e74abb32 XL |
75 | macro_rules! cfg_has_statx { |
76 | ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => { | |
77 | cfg_if::cfg_if! { | |
dfeec247 | 78 | if #[cfg(all(target_os = "linux", target_env = "gnu"))] { |
e74abb32 XL |
79 | $($then_tt)* |
80 | } else { | |
81 | $($else_tt)* | |
82 | } | |
83 | } | |
84 | }; | |
85 | ($($block_inner:tt)*) => { | |
dfeec247 | 86 | #[cfg(all(target_os = "linux", target_env = "gnu"))] |
e74abb32 XL |
87 | { |
88 | $($block_inner)* | |
89 | } | |
90 | }; | |
85aaf69f SL |
91 | } |
92 | ||
e74abb32 XL |
93 | cfg_has_statx! {{ |
94 | #[derive(Clone)] | |
95 | pub struct FileAttr { | |
96 | stat: stat64, | |
97 | statx_extra_fields: Option<StatxExtraFields>, | |
98 | } | |
99 | ||
100 | #[derive(Clone)] | |
101 | struct StatxExtraFields { | |
102 | // This is needed to check if btime is supported by the filesystem. | |
103 | stx_mask: u32, | |
104 | stx_btime: libc::statx_timestamp, | |
105 | } | |
106 | ||
107 | // We prefer `statx` on Linux if available, which contains file creation time. | |
108 | // Default `stat64` contains no creation time. | |
109 | unsafe fn try_statx( | |
110 | fd: c_int, | |
136023e0 | 111 | path: *const c_char, |
e74abb32 XL |
112 | flags: i32, |
113 | mask: u32, | |
114 | ) -> Option<io::Result<FileAttr>> { | |
115 | use crate::sync::atomic::{AtomicU8, Ordering}; | |
116 | ||
117 | // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx` | |
118 | // We store the availability in global to avoid unnecessary syscalls. | |
119 | // 0: Unknown | |
120 | // 1: Not available | |
121 | // 2: Available | |
122 | static STATX_STATE: AtomicU8 = AtomicU8::new(0); | |
123 | syscall! { | |
124 | fn statx( | |
125 | fd: c_int, | |
136023e0 | 126 | pathname: *const c_char, |
e74abb32 XL |
127 | flags: c_int, |
128 | mask: libc::c_uint, | |
129 | statxbuf: *mut libc::statx | |
130 | ) -> c_int | |
131 | } | |
132 | ||
133 | match STATX_STATE.load(Ordering::Relaxed) { | |
134 | 0 => { | |
17df50a5 | 135 | // It is a trick to call `statx` with null pointers to check if the syscall |
e74abb32 XL |
136 | // is available. According to the manual, it is expected to fail with EFAULT. |
137 | // We do this mainly for performance, since it is nearly hundreds times | |
60c5eb7d | 138 | // faster than a normal successful call. |
e74abb32 XL |
139 | let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut())) |
140 | .err() | |
141 | .and_then(|e| e.raw_os_error()); | |
142 | // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited | |
143 | // and returns `EPERM`. Listing all possible errors seems not a good idea. | |
144 | // See: https://github.com/rust-lang/rust/issues/65662 | |
145 | if err != Some(libc::EFAULT) { | |
146 | STATX_STATE.store(1, Ordering::Relaxed); | |
147 | return None; | |
148 | } | |
149 | STATX_STATE.store(2, Ordering::Relaxed); | |
150 | } | |
151 | 1 => return None, | |
152 | _ => {} | |
153 | } | |
154 | ||
155 | let mut buf: libc::statx = mem::zeroed(); | |
156 | if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) { | |
157 | return Some(Err(err)); | |
158 | } | |
159 | ||
160 | // We cannot fill `stat64` exhaustively because of private padding fields. | |
161 | let mut stat: stat64 = mem::zeroed(); | |
162 | // `c_ulong` on gnu-mips, `dev_t` otherwise | |
163 | stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _; | |
164 | stat.st_ino = buf.stx_ino as libc::ino64_t; | |
165 | stat.st_nlink = buf.stx_nlink as libc::nlink_t; | |
166 | stat.st_mode = buf.stx_mode as libc::mode_t; | |
167 | stat.st_uid = buf.stx_uid as libc::uid_t; | |
168 | stat.st_gid = buf.stx_gid as libc::gid_t; | |
169 | stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _; | |
170 | stat.st_size = buf.stx_size as off64_t; | |
171 | stat.st_blksize = buf.stx_blksize as libc::blksize_t; | |
172 | stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t; | |
173 | stat.st_atime = buf.stx_atime.tv_sec as libc::time_t; | |
174 | // `i64` on gnu-x86_64-x32, `c_ulong` otherwise. | |
175 | stat.st_atime_nsec = buf.stx_atime.tv_nsec as _; | |
176 | stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t; | |
177 | stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _; | |
178 | stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t; | |
179 | stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _; | |
180 | ||
181 | let extra = StatxExtraFields { | |
182 | stx_mask: buf.stx_mask, | |
183 | stx_btime: buf.stx_btime, | |
184 | }; | |
185 | ||
186 | Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) })) | |
187 | } | |
188 | ||
189 | } else { | |
190 | #[derive(Clone)] | |
191 | pub struct FileAttr { | |
192 | stat: stat64, | |
193 | } | |
194 | }} | |
195 | ||
94b46f34 XL |
196 | // all DirEntry's will have a reference to this struct |
197 | struct InnerReadDir { | |
c34b1796 | 198 | dirp: Dir, |
94b46f34 | 199 | root: PathBuf, |
85aaf69f SL |
200 | } |
201 | ||
8faf50e0 XL |
202 | pub struct ReadDir { |
203 | inner: Arc<InnerReadDir>, | |
29967ef6 XL |
204 | #[cfg(not(any( |
205 | target_os = "solaris", | |
206 | target_os = "illumos", | |
207 | target_os = "fuchsia", | |
208 | target_os = "redox", | |
209 | )))] | |
8faf50e0 XL |
210 | end_of_stream: bool, |
211 | } | |
94b46f34 | 212 | |
c34b1796 AL |
213 | struct Dir(*mut libc::DIR); |
214 | ||
215 | unsafe impl Send for Dir {} | |
216 | unsafe impl Sync for Dir {} | |
217 | ||
85aaf69f | 218 | pub struct DirEntry { |
7453a54e | 219 | entry: dirent64, |
29967ef6 | 220 | dir: Arc<InnerReadDir>, |
94b46f34 | 221 | // We need to store an owned copy of the entry name |
8bb4bdeb XL |
222 | // on Solaris and Fuchsia because a) it uses a zero-length |
223 | // array to store the name, b) its lifetime between readdir | |
224 | // calls is not guaranteed. | |
ba9703b0 XL |
225 | #[cfg(any( |
226 | target_os = "solaris", | |
227 | target_os = "illumos", | |
228 | target_os = "fuchsia", | |
229 | target_os = "redox" | |
230 | ))] | |
dfeec247 | 231 | name: Box<[u8]>, |
85aaf69f SL |
232 | } |
233 | ||
32a655c1 | 234 | #[derive(Clone, Debug)] |
85aaf69f | 235 | pub struct OpenOptions { |
7453a54e | 236 | // generic |
85aaf69f SL |
237 | read: bool, |
238 | write: bool, | |
7453a54e SL |
239 | append: bool, |
240 | truncate: bool, | |
241 | create: bool, | |
242 | create_new: bool, | |
243 | // system-specific | |
244 | custom_flags: i32, | |
85aaf69f SL |
245 | mode: mode_t, |
246 | } | |
247 | ||
248 | #[derive(Clone, PartialEq, Eq, Debug)] | |
dfeec247 XL |
249 | pub struct FilePermissions { |
250 | mode: mode_t, | |
251 | } | |
85aaf69f | 252 | |
5bcae85e | 253 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] |
dfeec247 XL |
254 | pub struct FileType { |
255 | mode: mode_t, | |
256 | } | |
d9579d0f | 257 | |
32a655c1 | 258 | #[derive(Debug)] |
dfeec247 XL |
259 | pub struct DirBuilder { |
260 | mode: mode_t, | |
261 | } | |
d9579d0f | 262 | |
e74abb32 XL |
263 | cfg_has_statx! {{ |
264 | impl FileAttr { | |
265 | fn from_stat64(stat: stat64) -> Self { | |
266 | Self { stat, statx_extra_fields: None } | |
267 | } | |
268 | } | |
269 | } else { | |
270 | impl FileAttr { | |
271 | fn from_stat64(stat: stat64) -> Self { | |
272 | Self { stat } | |
273 | } | |
274 | } | |
275 | }} | |
276 | ||
85aaf69f | 277 | impl FileAttr { |
dfeec247 XL |
278 | pub fn size(&self) -> u64 { |
279 | self.stat.st_size as u64 | |
280 | } | |
85aaf69f | 281 | pub fn perm(&self) -> FilePermissions { |
ea8adc8c | 282 | FilePermissions { mode: (self.stat.st_mode as mode_t) } |
85aaf69f SL |
283 | } |
284 | ||
d9579d0f AL |
285 | pub fn file_type(&self) -> FileType { |
286 | FileType { mode: self.stat.st_mode as mode_t } | |
85aaf69f | 287 | } |
d9579d0f | 288 | } |
85aaf69f | 289 | |
7453a54e SL |
290 | #[cfg(target_os = "netbsd")] |
291 | impl FileAttr { | |
292 | pub fn modified(&self) -> io::Result<SystemTime> { | |
293 | Ok(SystemTime::from(libc::timespec { | |
294 | tv_sec: self.stat.st_mtime as libc::time_t, | |
295 | tv_nsec: self.stat.st_mtimensec as libc::c_long, | |
296 | })) | |
297 | } | |
298 | ||
299 | pub fn accessed(&self) -> io::Result<SystemTime> { | |
300 | Ok(SystemTime::from(libc::timespec { | |
301 | tv_sec: self.stat.st_atime as libc::time_t, | |
302 | tv_nsec: self.stat.st_atimensec as libc::c_long, | |
303 | })) | |
304 | } | |
305 | ||
306 | pub fn created(&self) -> io::Result<SystemTime> { | |
307 | Ok(SystemTime::from(libc::timespec { | |
308 | tv_sec: self.stat.st_birthtime as libc::time_t, | |
309 | tv_nsec: self.stat.st_birthtimensec as libc::c_long, | |
310 | })) | |
311 | } | |
d9579d0f AL |
312 | } |
313 | ||
a7813a04 | 314 | #[cfg(not(target_os = "netbsd"))] |
7453a54e | 315 | impl FileAttr { |
94222f64 | 316 | #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))] |
7453a54e SL |
317 | pub fn modified(&self) -> io::Result<SystemTime> { |
318 | Ok(SystemTime::from(libc::timespec { | |
319 | tv_sec: self.stat.st_mtime as libc::time_t, | |
abe05a73 | 320 | tv_nsec: self.stat.st_mtime_nsec as _, |
7453a54e SL |
321 | })) |
322 | } | |
323 | ||
94222f64 | 324 | #[cfg(any(target_os = "vxworks", target_os = "espidf"))] |
29967ef6 XL |
325 | pub fn modified(&self) -> io::Result<SystemTime> { |
326 | Ok(SystemTime::from(libc::timespec { | |
327 | tv_sec: self.stat.st_mtime as libc::time_t, | |
328 | tv_nsec: 0, | |
329 | })) | |
330 | } | |
331 | ||
94222f64 | 332 | #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))] |
7453a54e SL |
333 | pub fn accessed(&self) -> io::Result<SystemTime> { |
334 | Ok(SystemTime::from(libc::timespec { | |
335 | tv_sec: self.stat.st_atime as libc::time_t, | |
abe05a73 | 336 | tv_nsec: self.stat.st_atime_nsec as _, |
7453a54e SL |
337 | })) |
338 | } | |
339 | ||
94222f64 | 340 | #[cfg(any(target_os = "vxworks", target_os = "espidf"))] |
29967ef6 XL |
341 | pub fn accessed(&self) -> io::Result<SystemTime> { |
342 | Ok(SystemTime::from(libc::timespec { | |
343 | tv_sec: self.stat.st_atime as libc::time_t, | |
344 | tv_nsec: 0, | |
345 | })) | |
346 | } | |
347 | ||
dfeec247 XL |
348 | #[cfg(any( |
349 | target_os = "freebsd", | |
350 | target_os = "openbsd", | |
351 | target_os = "macos", | |
352 | target_os = "ios" | |
353 | ))] | |
7453a54e SL |
354 | pub fn created(&self) -> io::Result<SystemTime> { |
355 | Ok(SystemTime::from(libc::timespec { | |
356 | tv_sec: self.stat.st_birthtime as libc::time_t, | |
357 | tv_nsec: self.stat.st_birthtime_nsec as libc::c_long, | |
358 | })) | |
359 | } | |
360 | ||
dfeec247 XL |
361 | #[cfg(not(any( |
362 | target_os = "freebsd", | |
363 | target_os = "openbsd", | |
364 | target_os = "macos", | |
365 | target_os = "ios" | |
366 | )))] | |
7453a54e | 367 | pub fn created(&self) -> io::Result<SystemTime> { |
e74abb32 XL |
368 | cfg_has_statx! { |
369 | if let Some(ext) = &self.statx_extra_fields { | |
370 | return if (ext.stx_mask & libc::STATX_BTIME) != 0 { | |
371 | Ok(SystemTime::from(libc::timespec { | |
372 | tv_sec: ext.stx_btime.tv_sec as libc::time_t, | |
373 | tv_nsec: ext.stx_btime.tv_nsec as _, | |
374 | })) | |
375 | } else { | |
cdc7bbd5 | 376 | Err(io::Error::new_const( |
136023e0 | 377 | io::ErrorKind::Uncategorized, |
cdc7bbd5 | 378 | &"creation time is not available for the filesystem", |
e74abb32 XL |
379 | )) |
380 | }; | |
381 | } | |
382 | } | |
383 | ||
cdc7bbd5 XL |
384 | Err(io::Error::new_const( |
385 | io::ErrorKind::Unsupported, | |
386 | &"creation time is not available on this platform \ | |
dfeec247 XL |
387 | currently", |
388 | )) | |
7453a54e SL |
389 | } |
390 | } | |
391 | ||
392 | impl AsInner<stat64> for FileAttr { | |
dfeec247 XL |
393 | fn as_inner(&self) -> &stat64 { |
394 | &self.stat | |
395 | } | |
85aaf69f SL |
396 | } |
397 | ||
398 | impl FilePermissions { | |
3b2f2976 XL |
399 | pub fn readonly(&self) -> bool { |
400 | // check if any class (owner, group, others) has write permission | |
401 | self.mode & 0o222 == 0 | |
402 | } | |
403 | ||
85aaf69f SL |
404 | pub fn set_readonly(&mut self, readonly: bool) { |
405 | if readonly { | |
3b2f2976 | 406 | // remove write permission for all classes; equivalent to `chmod a-w <file>` |
85aaf69f SL |
407 | self.mode &= !0o222; |
408 | } else { | |
3b2f2976 | 409 | // add write permission for all classes; equivalent to `chmod a+w <file>` |
85aaf69f SL |
410 | self.mode |= 0o222; |
411 | } | |
412 | } | |
dfeec247 XL |
413 | pub fn mode(&self) -> u32 { |
414 | self.mode as u32 | |
415 | } | |
d9579d0f AL |
416 | } |
417 | ||
418 | impl FileType { | |
dfeec247 XL |
419 | pub fn is_dir(&self) -> bool { |
420 | self.is(libc::S_IFDIR) | |
421 | } | |
422 | pub fn is_file(&self) -> bool { | |
423 | self.is(libc::S_IFREG) | |
424 | } | |
425 | pub fn is_symlink(&self) -> bool { | |
426 | self.is(libc::S_IFLNK) | |
427 | } | |
d9579d0f | 428 | |
dfeec247 XL |
429 | pub fn is(&self, mode: mode_t) -> bool { |
430 | self.mode & libc::S_IFMT == mode | |
431 | } | |
85aaf69f SL |
432 | } |
433 | ||
7453a54e SL |
434 | impl FromInner<u32> for FilePermissions { |
435 | fn from_inner(mode: u32) -> FilePermissions { | |
85aaf69f SL |
436 | FilePermissions { mode: mode as mode_t } |
437 | } | |
438 | } | |
439 | ||
c30ab7b3 | 440 | impl fmt::Debug for ReadDir { |
532ac7d7 | 441 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
c30ab7b3 SL |
442 | // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. |
443 | // Thus the result will be e g 'ReadDir("/home")' | |
8faf50e0 | 444 | fmt::Debug::fmt(&*self.inner.root, f) |
c30ab7b3 SL |
445 | } |
446 | } | |
447 | ||
85aaf69f SL |
448 | impl Iterator for ReadDir { |
449 | type Item = io::Result<DirEntry>; | |
450 | ||
ba9703b0 XL |
451 | #[cfg(any( |
452 | target_os = "solaris", | |
453 | target_os = "fuchsia", | |
454 | target_os = "redox", | |
455 | target_os = "illumos" | |
456 | ))] | |
7453a54e | 457 | fn next(&mut self) -> Option<io::Result<DirEntry>> { |
532ac7d7 XL |
458 | use crate::slice; |
459 | ||
7453a54e SL |
460 | unsafe { |
461 | loop { | |
462 | // Although readdir_r(3) would be a correct function to use here because | |
8bb4bdeb XL |
463 | // of the thread safety, on Illumos and Fuchsia the readdir(3C) function |
464 | // is safe to use in threaded applications and it is generally preferred | |
465 | // over the readdir_r(3C) function. | |
5bcae85e | 466 | super::os::set_errno(0); |
8faf50e0 | 467 | let entry_ptr = libc::readdir(self.inner.dirp.0); |
7453a54e | 468 | if entry_ptr.is_null() { |
17df50a5 | 469 | // null can mean either the end is reached or an error occurred. |
5bcae85e SL |
470 | // So we had to clear errno beforehand to check for an error now. |
471 | return match super::os::errno() { | |
472 | 0 => None, | |
473 | e => Some(Err(Error::from_raw_os_error(e))), | |
dfeec247 | 474 | }; |
7453a54e SL |
475 | } |
476 | ||
477 | let name = (*entry_ptr).d_name.as_ptr(); | |
478 | let namelen = libc::strlen(name) as usize; | |
479 | ||
480 | let ret = DirEntry { | |
481 | entry: *entry_ptr, | |
dfeec247 XL |
482 | name: slice::from_raw_parts(name as *const u8, namelen as usize) |
483 | .to_owned() | |
484 | .into_boxed_slice(), | |
29967ef6 | 485 | dir: Arc::clone(&self.inner), |
7453a54e SL |
486 | }; |
487 | if ret.name_bytes() != b"." && ret.name_bytes() != b".." { | |
dfeec247 | 488 | return Some(Ok(ret)); |
7453a54e SL |
489 | } |
490 | } | |
491 | } | |
492 | } | |
493 | ||
ba9703b0 XL |
494 | #[cfg(not(any( |
495 | target_os = "solaris", | |
496 | target_os = "fuchsia", | |
497 | target_os = "redox", | |
498 | target_os = "illumos" | |
499 | )))] | |
85aaf69f | 500 | fn next(&mut self) -> Option<io::Result<DirEntry>> { |
8faf50e0 XL |
501 | if self.end_of_stream { |
502 | return None; | |
503 | } | |
504 | ||
9cc50fc6 | 505 | unsafe { |
29967ef6 | 506 | let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) }; |
9cc50fc6 SL |
507 | let mut entry_ptr = ptr::null_mut(); |
508 | loop { | |
94222f64 XL |
509 | let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr); |
510 | if err != 0 { | |
8faf50e0 XL |
511 | if entry_ptr.is_null() { |
512 | // We encountered an error (which will be returned in this iteration), but | |
513 | // we also reached the end of the directory stream. The `end_of_stream` | |
514 | // flag is enabled to make sure that we return `None` in the next iteration | |
515 | // (instead of looping forever) | |
516 | self.end_of_stream = true; | |
517 | } | |
94222f64 | 518 | return Some(Err(Error::from_raw_os_error(err))); |
9cc50fc6 SL |
519 | } |
520 | if entry_ptr.is_null() { | |
dfeec247 | 521 | return None; |
9cc50fc6 SL |
522 | } |
523 | if ret.name_bytes() != b"." && ret.name_bytes() != b".." { | |
dfeec247 | 524 | return Some(Ok(ret)); |
9cc50fc6 | 525 | } |
85aaf69f SL |
526 | } |
527 | } | |
528 | } | |
529 | } | |
530 | ||
c34b1796 | 531 | impl Drop for Dir { |
85aaf69f | 532 | fn drop(&mut self) { |
c34b1796 | 533 | let r = unsafe { libc::closedir(self.0) }; |
85aaf69f SL |
534 | debug_assert_eq!(r, 0); |
535 | } | |
536 | } | |
537 | ||
538 | impl DirEntry { | |
539 | pub fn path(&self) -> PathBuf { | |
29967ef6 | 540 | self.dir.root.join(OsStr::from_bytes(self.name_bytes())) |
85aaf69f SL |
541 | } |
542 | ||
d9579d0f AL |
543 | pub fn file_name(&self) -> OsString { |
544 | OsStr::from_bytes(self.name_bytes()).to_os_string() | |
545 | } | |
546 | ||
94b46f34 XL |
547 | #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] |
548 | pub fn metadata(&self) -> io::Result<FileAttr> { | |
29967ef6 | 549 | let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; |
e74abb32 XL |
550 | let name = self.entry.d_name.as_ptr(); |
551 | ||
552 | cfg_has_statx! { | |
553 | if let Some(ret) = unsafe { try_statx( | |
554 | fd, | |
555 | name, | |
556 | libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, | |
557 | libc::STATX_ALL, | |
558 | ) } { | |
559 | return ret; | |
560 | } | |
561 | } | |
562 | ||
94b46f34 | 563 | let mut stat: stat64 = unsafe { mem::zeroed() }; |
dfeec247 | 564 | cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?; |
e74abb32 | 565 | Ok(FileAttr::from_stat64(stat)) |
94b46f34 XL |
566 | } |
567 | ||
568 | #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))] | |
d9579d0f AL |
569 | pub fn metadata(&self) -> io::Result<FileAttr> { |
570 | lstat(&self.path()) | |
571 | } | |
572 | ||
29967ef6 XL |
573 | #[cfg(any( |
574 | target_os = "solaris", | |
575 | target_os = "illumos", | |
576 | target_os = "haiku", | |
577 | target_os = "vxworks" | |
578 | ))] | |
9e0c209e SL |
579 | pub fn file_type(&self) -> io::Result<FileType> { |
580 | lstat(&self.path()).map(|m| m.file_type()) | |
581 | } | |
582 | ||
29967ef6 XL |
583 | #[cfg(not(any( |
584 | target_os = "solaris", | |
585 | target_os = "illumos", | |
586 | target_os = "haiku", | |
587 | target_os = "vxworks" | |
588 | )))] | |
d9579d0f | 589 | pub fn file_type(&self) -> io::Result<FileType> { |
9cc50fc6 SL |
590 | match self.entry.d_type { |
591 | libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), | |
592 | libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }), | |
593 | libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }), | |
594 | libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }), | |
595 | libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }), | |
596 | libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }), | |
597 | libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }), | |
598 | _ => lstat(&self.path()).map(|m| m.file_type()), | |
d9579d0f AL |
599 | } |
600 | } | |
601 | ||
dfeec247 XL |
602 | #[cfg(any( |
603 | target_os = "macos", | |
604 | target_os = "ios", | |
605 | target_os = "linux", | |
606 | target_os = "emscripten", | |
607 | target_os = "android", | |
608 | target_os = "solaris", | |
ba9703b0 | 609 | target_os = "illumos", |
dfeec247 XL |
610 | target_os = "haiku", |
611 | target_os = "l4re", | |
612 | target_os = "fuchsia", | |
29967ef6 | 613 | target_os = "redox", |
94222f64 XL |
614 | target_os = "vxworks", |
615 | target_os = "espidf" | |
dfeec247 | 616 | ))] |
7453a54e SL |
617 | pub fn ino(&self) -> u64 { |
618 | self.entry.d_ino as u64 | |
9cc50fc6 SL |
619 | } |
620 | ||
dfeec247 XL |
621 | #[cfg(any( |
622 | target_os = "freebsd", | |
623 | target_os = "openbsd", | |
624 | target_os = "netbsd", | |
625 | target_os = "dragonfly" | |
626 | ))] | |
7453a54e SL |
627 | pub fn ino(&self) -> u64 { |
628 | self.entry.d_fileno as u64 | |
d9579d0f AL |
629 | } |
630 | ||
dfeec247 XL |
631 | #[cfg(any( |
632 | target_os = "macos", | |
633 | target_os = "ios", | |
634 | target_os = "netbsd", | |
635 | target_os = "openbsd", | |
636 | target_os = "freebsd", | |
637 | target_os = "dragonfly" | |
638 | ))] | |
9cc50fc6 | 639 | fn name_bytes(&self) -> &[u8] { |
532ac7d7 | 640 | use crate::slice; |
85aaf69f | 641 | unsafe { |
dfeec247 XL |
642 | slice::from_raw_parts( |
643 | self.entry.d_name.as_ptr() as *const u8, | |
644 | self.entry.d_namlen as usize, | |
645 | ) | |
85aaf69f SL |
646 | } |
647 | } | |
dfeec247 XL |
648 | #[cfg(any( |
649 | target_os = "android", | |
650 | target_os = "linux", | |
651 | target_os = "emscripten", | |
652 | target_os = "l4re", | |
29967ef6 | 653 | target_os = "haiku", |
94222f64 XL |
654 | target_os = "vxworks", |
655 | target_os = "espidf" | |
dfeec247 | 656 | ))] |
9cc50fc6 | 657 | fn name_bytes(&self) -> &[u8] { |
dfeec247 | 658 | unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() } |
c34b1796 | 659 | } |
ba9703b0 XL |
660 | #[cfg(any( |
661 | target_os = "solaris", | |
662 | target_os = "illumos", | |
663 | target_os = "fuchsia", | |
664 | target_os = "redox" | |
665 | ))] | |
7453a54e SL |
666 | fn name_bytes(&self) -> &[u8] { |
667 | &*self.name | |
668 | } | |
136023e0 XL |
669 | |
670 | pub fn file_name_os_str(&self) -> &OsStr { | |
671 | OsStr::from_bytes(self.name_bytes()) | |
672 | } | |
85aaf69f SL |
673 | } |
674 | ||
675 | impl OpenOptions { | |
676 | pub fn new() -> OpenOptions { | |
677 | OpenOptions { | |
7453a54e | 678 | // generic |
85aaf69f SL |
679 | read: false, |
680 | write: false, | |
7453a54e SL |
681 | append: false, |
682 | truncate: false, | |
683 | create: false, | |
684 | create_new: false, | |
685 | // system-specific | |
686 | custom_flags: 0, | |
85aaf69f SL |
687 | mode: 0o666, |
688 | } | |
689 | } | |
690 | ||
dfeec247 XL |
691 | pub fn read(&mut self, read: bool) { |
692 | self.read = read; | |
693 | } | |
694 | pub fn write(&mut self, write: bool) { | |
695 | self.write = write; | |
696 | } | |
697 | pub fn append(&mut self, append: bool) { | |
698 | self.append = append; | |
699 | } | |
700 | pub fn truncate(&mut self, truncate: bool) { | |
701 | self.truncate = truncate; | |
702 | } | |
703 | pub fn create(&mut self, create: bool) { | |
704 | self.create = create; | |
705 | } | |
706 | pub fn create_new(&mut self, create_new: bool) { | |
707 | self.create_new = create_new; | |
708 | } | |
7453a54e | 709 | |
dfeec247 XL |
710 | pub fn custom_flags(&mut self, flags: i32) { |
711 | self.custom_flags = flags; | |
712 | } | |
713 | pub fn mode(&mut self, mode: u32) { | |
714 | self.mode = mode as mode_t; | |
715 | } | |
7453a54e SL |
716 | |
717 | fn get_access_mode(&self) -> io::Result<c_int> { | |
718 | match (self.read, self.write, self.append) { | |
dfeec247 XL |
719 | (true, false, false) => Ok(libc::O_RDONLY), |
720 | (false, true, false) => Ok(libc::O_WRONLY), | |
721 | (true, true, false) => Ok(libc::O_RDWR), | |
722 | (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), | |
723 | (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), | |
7453a54e SL |
724 | (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), |
725 | } | |
85aaf69f SL |
726 | } |
727 | ||
7453a54e SL |
728 | fn get_creation_mode(&self) -> io::Result<c_int> { |
729 | match (self.write, self.append) { | |
730 | (true, false) => {} | |
dfeec247 | 731 | (false, false) => { |
7453a54e SL |
732 | if self.truncate || self.create || self.create_new { |
733 | return Err(Error::from_raw_os_error(libc::EINVAL)); | |
dfeec247 XL |
734 | } |
735 | } | |
736 | (_, true) => { | |
7453a54e SL |
737 | if self.truncate && !self.create_new { |
738 | return Err(Error::from_raw_os_error(libc::EINVAL)); | |
dfeec247 XL |
739 | } |
740 | } | |
85aaf69f | 741 | } |
7453a54e SL |
742 | |
743 | Ok(match (self.create, self.truncate, self.create_new) { | |
dfeec247 XL |
744 | (false, false, false) => 0, |
745 | (true, false, false) => libc::O_CREAT, | |
746 | (false, true, false) => libc::O_TRUNC, | |
747 | (true, true, false) => libc::O_CREAT | libc::O_TRUNC, | |
748 | (_, _, true) => libc::O_CREAT | libc::O_EXCL, | |
749 | }) | |
85aaf69f SL |
750 | } |
751 | } | |
752 | ||
753 | impl File { | |
754 | pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { | |
54a0048b | 755 | let path = cstr(path)?; |
9346a6ac AL |
756 | File::open_c(&path, opts) |
757 | } | |
758 | ||
759 | pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> { | |
dfeec247 XL |
760 | let flags = libc::O_CLOEXEC |
761 | | opts.get_access_mode()? | |
762 | | opts.get_creation_mode()? | |
763 | | (opts.custom_flags as c_int & !libc::O_ACCMODE); | |
f9f354fc XL |
764 | // The third argument of `open64` is documented to have type `mode_t`. On |
765 | // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`. | |
766 | // However, since this is a variadic function, C integer promotion rules mean that on | |
767 | // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms). | |
dfeec247 | 768 | let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; |
94222f64 | 769 | Ok(File(unsafe { FileDesc::from_raw_fd(fd) })) |
85aaf69f SL |
770 | } |
771 | ||
772 | pub fn file_attr(&self) -> io::Result<FileAttr> { | |
94222f64 | 773 | let fd = self.as_raw_fd(); |
e74abb32 XL |
774 | |
775 | cfg_has_statx! { | |
776 | if let Some(ret) = unsafe { try_statx( | |
777 | fd, | |
136023e0 | 778 | b"\0" as *const _ as *const c_char, |
e74abb32 XL |
779 | libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, |
780 | libc::STATX_ALL, | |
781 | ) } { | |
782 | return ret; | |
783 | } | |
784 | } | |
785 | ||
7453a54e | 786 | let mut stat: stat64 = unsafe { mem::zeroed() }; |
dfeec247 | 787 | cvt(unsafe { fstat64(fd, &mut stat) })?; |
e74abb32 | 788 | Ok(FileAttr::from_stat64(stat)) |
85aaf69f SL |
789 | } |
790 | ||
791 | pub fn fsync(&self) -> io::Result<()> { | |
94222f64 | 792 | cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?; |
48663c56 XL |
793 | return Ok(()); |
794 | ||
795 | #[cfg(any(target_os = "macos", target_os = "ios"))] | |
796 | unsafe fn os_fsync(fd: c_int) -> c_int { | |
797 | libc::fcntl(fd, libc::F_FULLFSYNC) | |
798 | } | |
799 | #[cfg(not(any(target_os = "macos", target_os = "ios")))] | |
dfeec247 XL |
800 | unsafe fn os_fsync(fd: c_int) -> c_int { |
801 | libc::fsync(fd) | |
802 | } | |
85aaf69f SL |
803 | } |
804 | ||
805 | pub fn datasync(&self) -> io::Result<()> { | |
94222f64 | 806 | cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; |
85aaf69f SL |
807 | return Ok(()); |
808 | ||
809 | #[cfg(any(target_os = "macos", target_os = "ios"))] | |
810 | unsafe fn os_datasync(fd: c_int) -> c_int { | |
811 | libc::fcntl(fd, libc::F_FULLFSYNC) | |
812 | } | |
29967ef6 XL |
813 | #[cfg(any( |
814 | target_os = "freebsd", | |
815 | target_os = "linux", | |
816 | target_os = "android", | |
817 | target_os = "netbsd", | |
818 | target_os = "openbsd" | |
819 | ))] | |
dfeec247 XL |
820 | unsafe fn os_datasync(fd: c_int) -> c_int { |
821 | libc::fdatasync(fd) | |
822 | } | |
29967ef6 XL |
823 | #[cfg(not(any( |
824 | target_os = "android", | |
825 | target_os = "freebsd", | |
826 | target_os = "ios", | |
827 | target_os = "linux", | |
828 | target_os = "macos", | |
829 | target_os = "netbsd", | |
830 | target_os = "openbsd" | |
831 | )))] | |
dfeec247 XL |
832 | unsafe fn os_datasync(fd: c_int) -> c_int { |
833 | libc::fsync(fd) | |
834 | } | |
85aaf69f SL |
835 | } |
836 | ||
837 | pub fn truncate(&self, size: u64) -> io::Result<()> { | |
a7813a04 | 838 | #[cfg(target_os = "android")] |
94222f64 | 839 | return crate::sys::android::ftruncate64(self.as_raw_fd(), size); |
a7813a04 XL |
840 | |
841 | #[cfg(not(target_os = "android"))] | |
416331ca XL |
842 | { |
843 | use crate::convert::TryInto; | |
dfeec247 XL |
844 | let size: off64_t = |
845 | size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; | |
94222f64 | 846 | cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop) |
416331ca | 847 | } |
85aaf69f SL |
848 | } |
849 | ||
850 | pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { | |
851 | self.0.read(buf) | |
852 | } | |
853 | ||
48663c56 XL |
854 | pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
855 | self.0.read_vectored(bufs) | |
856 | } | |
857 | ||
f9f354fc XL |
858 | #[inline] |
859 | pub fn is_read_vectored(&self) -> bool { | |
860 | self.0.is_read_vectored() | |
861 | } | |
862 | ||
c30ab7b3 SL |
863 | pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { |
864 | self.0.read_at(buf, offset) | |
865 | } | |
866 | ||
85aaf69f SL |
867 | pub fn write(&self, buf: &[u8]) -> io::Result<usize> { |
868 | self.0.write(buf) | |
869 | } | |
870 | ||
48663c56 XL |
871 | pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
872 | self.0.write_vectored(bufs) | |
873 | } | |
874 | ||
f9f354fc XL |
875 | #[inline] |
876 | pub fn is_write_vectored(&self) -> bool { | |
877 | self.0.is_write_vectored() | |
878 | } | |
879 | ||
c30ab7b3 SL |
880 | pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { |
881 | self.0.write_at(buf, offset) | |
882 | } | |
883 | ||
dfeec247 XL |
884 | pub fn flush(&self) -> io::Result<()> { |
885 | Ok(()) | |
886 | } | |
85aaf69f SL |
887 | |
888 | pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { | |
889 | let (whence, pos) = match pos { | |
5bcae85e SL |
890 | // Casting to `i64` is fine, too large values will end up as |
891 | // negative which will cause an error in `lseek64`. | |
892 | SeekFrom::Start(off) => (libc::SEEK_SET, off as i64), | |
893 | SeekFrom::End(off) => (libc::SEEK_END, off), | |
894 | SeekFrom::Current(off) => (libc::SEEK_CUR, off), | |
85aaf69f | 895 | }; |
94222f64 | 896 | let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?; |
85aaf69f SL |
897 | Ok(n as u64) |
898 | } | |
899 | ||
7453a54e SL |
900 | pub fn duplicate(&self) -> io::Result<File> { |
901 | self.0.duplicate().map(File) | |
902 | } | |
903 | ||
476ff2be | 904 | pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { |
94222f64 | 905 | cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?; |
476ff2be SL |
906 | Ok(()) |
907 | } | |
85aaf69f SL |
908 | } |
909 | ||
d9579d0f AL |
910 | impl DirBuilder { |
911 | pub fn new() -> DirBuilder { | |
912 | DirBuilder { mode: 0o777 } | |
913 | } | |
914 | ||
915 | pub fn mkdir(&self, p: &Path) -> io::Result<()> { | |
54a0048b SL |
916 | let p = cstr(p)?; |
917 | cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; | |
d9579d0f AL |
918 | Ok(()) |
919 | } | |
920 | ||
7453a54e SL |
921 | pub fn set_mode(&mut self, mode: u32) { |
922 | self.mode = mode as mode_t; | |
d9579d0f AL |
923 | } |
924 | } | |
925 | ||
85aaf69f | 926 | fn cstr(path: &Path) -> io::Result<CString> { |
54a0048b | 927 | Ok(CString::new(path.as_os_str().as_bytes())?) |
c34b1796 AL |
928 | } |
929 | ||
94222f64 XL |
930 | impl AsInner<FileDesc> for File { |
931 | fn as_inner(&self) -> &FileDesc { | |
932 | &self.0 | |
933 | } | |
934 | } | |
935 | ||
936 | impl AsInnerMut<FileDesc> for File { | |
937 | fn as_inner_mut(&mut self) -> &mut FileDesc { | |
938 | &mut self.0 | |
939 | } | |
940 | } | |
941 | ||
942 | impl IntoInner<FileDesc> for File { | |
943 | fn into_inner(self) -> FileDesc { | |
944 | self.0 | |
945 | } | |
946 | } | |
947 | ||
948 | impl FromInner<FileDesc> for File { | |
949 | fn from_inner(file_desc: FileDesc) -> Self { | |
950 | Self(file_desc) | |
951 | } | |
952 | } | |
953 | ||
954 | impl AsFd for File { | |
955 | fn as_fd(&self) -> BorrowedFd<'_> { | |
956 | self.0.as_fd() | |
957 | } | |
958 | } | |
959 | ||
960 | impl AsRawFd for File { | |
961 | fn as_raw_fd(&self) -> RawFd { | |
962 | self.0.as_raw_fd() | |
963 | } | |
964 | } | |
965 | ||
966 | impl IntoRawFd for File { | |
967 | fn into_raw_fd(self) -> RawFd { | |
968 | self.0.into_raw_fd() | |
969 | } | |
970 | } | |
971 | ||
972 | impl FromRawFd for File { | |
973 | unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { | |
974 | Self(FromRawFd::from_raw_fd(raw_fd)) | |
c34b1796 | 975 | } |
85aaf69f SL |
976 | } |
977 | ||
d9579d0f | 978 | impl fmt::Debug for File { |
532ac7d7 | 979 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
94222f64 | 980 | #[cfg(any(target_os = "linux", target_os = "netbsd"))] |
d9579d0f | 981 | fn get_path(fd: c_int) -> Option<PathBuf> { |
d9579d0f AL |
982 | let mut p = PathBuf::from("/proc/self/fd"); |
983 | p.push(&fd.to_string()); | |
984 | readlink(&p).ok() | |
985 | } | |
986 | ||
c1a9b12d SL |
987 | #[cfg(target_os = "macos")] |
988 | fn get_path(fd: c_int) -> Option<PathBuf> { | |
e9174d1e | 989 | // FIXME: The use of PATH_MAX is generally not encouraged, but it |
cc61c64b | 990 | // is inevitable in this case because macOS defines `fcntl` with |
e9174d1e SL |
991 | // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no |
992 | // alternatives. If a better method is invented, it should be used | |
993 | // instead. | |
dfeec247 | 994 | let mut buf = vec![0; libc::PATH_MAX as usize]; |
c1a9b12d SL |
995 | let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; |
996 | if n == -1 { | |
997 | return None; | |
998 | } | |
999 | let l = buf.iter().position(|&c| c == 0).unwrap(); | |
1000 | buf.truncate(l as usize); | |
e9174d1e | 1001 | buf.shrink_to_fit(); |
c1a9b12d SL |
1002 | Some(PathBuf::from(OsString::from_vec(buf))) |
1003 | } | |
1004 | ||
29967ef6 XL |
1005 | #[cfg(target_os = "vxworks")] |
1006 | fn get_path(fd: c_int) -> Option<PathBuf> { | |
1007 | let mut buf = vec![0; libc::PATH_MAX as usize]; | |
1008 | let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) }; | |
1009 | if n == -1 { | |
1010 | return None; | |
1011 | } | |
1012 | let l = buf.iter().position(|&c| c == 0).unwrap(); | |
1013 | buf.truncate(l as usize); | |
1014 | Some(PathBuf::from(OsString::from_vec(buf))) | |
1015 | } | |
1016 | ||
94222f64 XL |
1017 | #[cfg(not(any( |
1018 | target_os = "linux", | |
1019 | target_os = "macos", | |
1020 | target_os = "vxworks", | |
1021 | target_os = "netbsd" | |
1022 | )))] | |
d9579d0f AL |
1023 | fn get_path(_fd: c_int) -> Option<PathBuf> { |
1024 | // FIXME(#24570): implement this for other Unix platforms | |
1025 | None | |
1026 | } | |
1027 | ||
29967ef6 | 1028 | #[cfg(any(target_os = "linux", target_os = "macos", target_os = "vxworks"))] |
d9579d0f AL |
1029 | fn get_mode(fd: c_int) -> Option<(bool, bool)> { |
1030 | let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; | |
1031 | if mode == -1 { | |
1032 | return None; | |
1033 | } | |
1034 | match mode & libc::O_ACCMODE { | |
1035 | libc::O_RDONLY => Some((true, false)), | |
1036 | libc::O_RDWR => Some((true, true)), | |
1037 | libc::O_WRONLY => Some((false, true)), | |
dfeec247 | 1038 | _ => None, |
d9579d0f AL |
1039 | } |
1040 | } | |
1041 | ||
29967ef6 | 1042 | #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))] |
d9579d0f AL |
1043 | fn get_mode(_fd: c_int) -> Option<(bool, bool)> { |
1044 | // FIXME(#24570): implement this for other Unix platforms | |
1045 | None | |
1046 | } | |
1047 | ||
94222f64 | 1048 | let fd = self.as_raw_fd(); |
62682a34 SL |
1049 | let mut b = f.debug_struct("File"); |
1050 | b.field("fd", &fd); | |
d9579d0f | 1051 | if let Some(path) = get_path(fd) { |
62682a34 | 1052 | b.field("path", &path); |
d9579d0f AL |
1053 | } |
1054 | if let Some((read, write)) = get_mode(fd) { | |
62682a34 | 1055 | b.field("read", &read).field("write", &write); |
d9579d0f AL |
1056 | } |
1057 | b.finish() | |
1058 | } | |
85aaf69f SL |
1059 | } |
1060 | ||
1061 | pub fn readdir(p: &Path) -> io::Result<ReadDir> { | |
94b46f34 | 1062 | let root = p.to_path_buf(); |
54a0048b | 1063 | let p = cstr(p)?; |
85aaf69f SL |
1064 | unsafe { |
1065 | let ptr = libc::opendir(p.as_ptr()); | |
1066 | if ptr.is_null() { | |
1067 | Err(Error::last_os_error()) | |
1068 | } else { | |
94b46f34 | 1069 | let inner = InnerReadDir { dirp: Dir(ptr), root }; |
29967ef6 XL |
1070 | Ok(ReadDir { |
1071 | inner: Arc::new(inner), | |
1072 | #[cfg(not(any( | |
1073 | target_os = "solaris", | |
1074 | target_os = "illumos", | |
1075 | target_os = "fuchsia", | |
1076 | target_os = "redox", | |
1077 | )))] | |
1078 | end_of_stream: false, | |
1079 | }) | |
85aaf69f SL |
1080 | } |
1081 | } | |
1082 | } | |
1083 | ||
1084 | pub fn unlink(p: &Path) -> io::Result<()> { | |
54a0048b SL |
1085 | let p = cstr(p)?; |
1086 | cvt(unsafe { libc::unlink(p.as_ptr()) })?; | |
85aaf69f SL |
1087 | Ok(()) |
1088 | } | |
1089 | ||
1090 | pub fn rename(old: &Path, new: &Path) -> io::Result<()> { | |
54a0048b SL |
1091 | let old = cstr(old)?; |
1092 | let new = cstr(new)?; | |
1093 | cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?; | |
85aaf69f SL |
1094 | Ok(()) |
1095 | } | |
1096 | ||
1097 | pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { | |
54a0048b SL |
1098 | let p = cstr(p)?; |
1099 | cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?; | |
85aaf69f SL |
1100 | Ok(()) |
1101 | } | |
1102 | ||
1103 | pub fn rmdir(p: &Path) -> io::Result<()> { | |
54a0048b SL |
1104 | let p = cstr(p)?; |
1105 | cvt(unsafe { libc::rmdir(p.as_ptr()) })?; | |
85aaf69f SL |
1106 | Ok(()) |
1107 | } | |
1108 | ||
85aaf69f | 1109 | pub fn readlink(p: &Path) -> io::Result<PathBuf> { |
54a0048b | 1110 | let c_path = cstr(p)?; |
85aaf69f | 1111 | let p = c_path.as_ptr(); |
e9174d1e SL |
1112 | |
1113 | let mut buf = Vec::with_capacity(256); | |
1114 | ||
1115 | loop { | |
dfeec247 XL |
1116 | let buf_read = |
1117 | cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize; | |
e9174d1e | 1118 | |
dfeec247 XL |
1119 | unsafe { |
1120 | buf.set_len(buf_read); | |
1121 | } | |
e9174d1e SL |
1122 | |
1123 | if buf_read != buf.capacity() { | |
1124 | buf.shrink_to_fit(); | |
1125 | ||
1126 | return Ok(PathBuf::from(OsString::from_vec(buf))); | |
1127 | } | |
1128 | ||
1129 | // Trigger the internal buffer resizing logic of `Vec` by requiring | |
1130 | // more space than the current capacity. The length is guaranteed to be | |
1131 | // the same as the capacity due to the if statement above. | |
1132 | buf.reserve(1); | |
85aaf69f | 1133 | } |
85aaf69f SL |
1134 | } |
1135 | ||
fc512014 XL |
1136 | pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { |
1137 | let original = cstr(original)?; | |
1138 | let link = cstr(link)?; | |
1139 | cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?; | |
85aaf69f SL |
1140 | Ok(()) |
1141 | } | |
1142 | ||
fc512014 XL |
1143 | pub fn link(original: &Path, link: &Path) -> io::Result<()> { |
1144 | let original = cstr(original)?; | |
1145 | let link = cstr(link)?; | |
29967ef6 | 1146 | cfg_if::cfg_if! { |
94222f64 XL |
1147 | if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf"))] { |
1148 | // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves | |
136023e0 XL |
1149 | // it implementation-defined whether `link` follows symlinks, so rely on the |
1150 | // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. | |
1151 | // Android has `linkat` on newer versions, but we happen to know `link` | |
1152 | // always has the correct behavior, so it's here as well. | |
fc512014 | 1153 | cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; |
136023e0 XL |
1154 | } else if #[cfg(target_os = "macos")] { |
1155 | // On MacOS, older versions (<=10.9) lack support for linkat while newer | |
1156 | // versions have it. We want to use linkat if it is available, so we use weak! | |
1157 | // to check. `linkat` is preferable to `link` ecause it gives us a flag to | |
1158 | // specify how symlinks should be handled. We pass 0 as the flags argument, | |
1159 | // meaning it shouldn't follow symlinks. | |
1160 | weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int); | |
1161 | ||
1162 | if let Some(f) = linkat.get() { | |
1163 | cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; | |
1164 | } else { | |
1165 | cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; | |
1166 | }; | |
29967ef6 | 1167 | } else { |
136023e0 XL |
1168 | // Where we can, use `linkat` instead of `link`; see the comment above |
1169 | // this one for details on why. | |
fc512014 | 1170 | cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; |
29967ef6 XL |
1171 | } |
1172 | } | |
85aaf69f SL |
1173 | Ok(()) |
1174 | } | |
1175 | ||
1176 | pub fn stat(p: &Path) -> io::Result<FileAttr> { | |
54a0048b | 1177 | let p = cstr(p)?; |
e74abb32 XL |
1178 | |
1179 | cfg_has_statx! { | |
1180 | if let Some(ret) = unsafe { try_statx( | |
1181 | libc::AT_FDCWD, | |
1182 | p.as_ptr(), | |
1183 | libc::AT_STATX_SYNC_AS_STAT, | |
1184 | libc::STATX_ALL, | |
1185 | ) } { | |
1186 | return ret; | |
1187 | } | |
1188 | } | |
1189 | ||
7453a54e | 1190 | let mut stat: stat64 = unsafe { mem::zeroed() }; |
dfeec247 | 1191 | cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?; |
e74abb32 | 1192 | Ok(FileAttr::from_stat64(stat)) |
85aaf69f SL |
1193 | } |
1194 | ||
1195 | pub fn lstat(p: &Path) -> io::Result<FileAttr> { | |
54a0048b | 1196 | let p = cstr(p)?; |
e74abb32 XL |
1197 | |
1198 | cfg_has_statx! { | |
1199 | if let Some(ret) = unsafe { try_statx( | |
1200 | libc::AT_FDCWD, | |
1201 | p.as_ptr(), | |
1202 | libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT, | |
1203 | libc::STATX_ALL, | |
1204 | ) } { | |
1205 | return ret; | |
1206 | } | |
1207 | } | |
1208 | ||
7453a54e | 1209 | let mut stat: stat64 = unsafe { mem::zeroed() }; |
dfeec247 | 1210 | cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?; |
e74abb32 | 1211 | Ok(FileAttr::from_stat64(stat)) |
85aaf69f SL |
1212 | } |
1213 | ||
d9579d0f | 1214 | pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { |
54a0048b | 1215 | let path = CString::new(p.as_os_str().as_bytes())?; |
e9174d1e | 1216 | let buf; |
d9579d0f | 1217 | unsafe { |
92a42be0 | 1218 | let r = libc::realpath(path.as_ptr(), ptr::null_mut()); |
d9579d0f | 1219 | if r.is_null() { |
dfeec247 | 1220 | return Err(io::Error::last_os_error()); |
d9579d0f | 1221 | } |
e9174d1e SL |
1222 | buf = CStr::from_ptr(r).to_bytes().to_vec(); |
1223 | libc::free(r as *mut _); | |
d9579d0f | 1224 | } |
d9579d0f AL |
1225 | Ok(PathBuf::from(OsString::from_vec(buf))) |
1226 | } | |
c1a9b12d | 1227 | |
48663c56 XL |
1228 | fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { |
1229 | use crate::fs::File; | |
cdc7bbd5 | 1230 | use crate::sys_common::fs::NOT_FILE_ERROR; |
48663c56 XL |
1231 | |
1232 | let reader = File::open(from)?; | |
1233 | let metadata = reader.metadata()?; | |
1234 | if !metadata.is_file() { | |
cdc7bbd5 | 1235 | return Err(NOT_FILE_ERROR); |
48663c56 XL |
1236 | } |
1237 | Ok((reader, metadata)) | |
1238 | } | |
1239 | ||
94222f64 XL |
1240 | #[cfg(target_os = "espidf")] |
1241 | fn open_to_and_set_permissions( | |
1242 | to: &Path, | |
1243 | reader_metadata: crate::fs::Metadata, | |
1244 | ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { | |
1245 | use crate::fs::OpenOptions; | |
1246 | let writer = OpenOptions::new().open(to)?; | |
1247 | let writer_metadata = writer.metadata()?; | |
1248 | Ok((writer, writer_metadata)) | |
1249 | } | |
1250 | ||
1251 | #[cfg(not(target_os = "espidf"))] | |
48663c56 | 1252 | fn open_to_and_set_permissions( |
532ac7d7 | 1253 | to: &Path, |
48663c56 XL |
1254 | reader_metadata: crate::fs::Metadata, |
1255 | ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { | |
1256 | use crate::fs::OpenOptions; | |
532ac7d7 XL |
1257 | use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt}; |
1258 | ||
48663c56 | 1259 | let perm = reader_metadata.permissions(); |
532ac7d7 XL |
1260 | let writer = OpenOptions::new() |
1261 | // create the file with the correct mode right away | |
1262 | .mode(perm.mode()) | |
1263 | .write(true) | |
1264 | .create(true) | |
1265 | .truncate(true) | |
1266 | .open(to)?; | |
1267 | let writer_metadata = writer.metadata()?; | |
1268 | if writer_metadata.is_file() { | |
1269 | // Set the correct file permissions, in case the file already existed. | |
1270 | // Don't set the permissions on already existing non-files like | |
1271 | // pipes/FIFOs or device nodes. | |
1272 | writer.set_permissions(perm)?; | |
c1a9b12d | 1273 | } |
48663c56 | 1274 | Ok((writer, writer_metadata)) |
532ac7d7 | 1275 | } |
c1a9b12d | 1276 | |
dfeec247 XL |
1277 | #[cfg(not(any( |
1278 | target_os = "linux", | |
1279 | target_os = "android", | |
1280 | target_os = "macos", | |
1281 | target_os = "ios" | |
1282 | )))] | |
532ac7d7 | 1283 | pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { |
48663c56 XL |
1284 | let (mut reader, reader_metadata) = open_from(from)?; |
1285 | let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; | |
c1a9b12d | 1286 | |
532ac7d7 | 1287 | io::copy(&mut reader, &mut writer) |
c1a9b12d | 1288 | } |
94b46f34 XL |
1289 | |
1290 | #[cfg(any(target_os = "linux", target_os = "android"))] | |
1291 | pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { | |
48663c56 | 1292 | let (mut reader, reader_metadata) = open_from(from)?; |
1b1a35ee | 1293 | let max_len = u64::MAX; |
48663c56 | 1294 | let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?; |
94b46f34 | 1295 | |
fc512014 XL |
1296 | use super::kernel_copy::{copy_regular_files, CopyResult}; |
1297 | ||
1298 | match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) { | |
1299 | CopyResult::Ended(bytes) => Ok(bytes), | |
1300 | CopyResult::Error(e, _) => Err(e), | |
1301 | CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) { | |
1302 | Ok(bytes) => Ok(bytes + written), | |
1303 | Err(e) => Err(e), | |
1304 | }, | |
94b46f34 | 1305 | } |
94b46f34 | 1306 | } |
532ac7d7 XL |
1307 | |
1308 | #[cfg(any(target_os = "macos", target_os = "ios"))] | |
1309 | pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { | |
48663c56 XL |
1310 | use crate::sync::atomic::{AtomicBool, Ordering}; |
1311 | ||
532ac7d7 XL |
1312 | const COPYFILE_ACL: u32 = 1 << 0; |
1313 | const COPYFILE_STAT: u32 = 1 << 1; | |
1314 | const COPYFILE_XATTR: u32 = 1 << 2; | |
1315 | const COPYFILE_DATA: u32 = 1 << 3; | |
1316 | ||
1317 | const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL; | |
1318 | const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR; | |
1319 | const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA; | |
1320 | ||
1321 | const COPYFILE_STATE_COPIED: u32 = 8; | |
1322 | ||
1323 | #[allow(non_camel_case_types)] | |
1324 | type copyfile_state_t = *mut libc::c_void; | |
1325 | #[allow(non_camel_case_types)] | |
1326 | type copyfile_flags_t = u32; | |
1327 | ||
1328 | extern "C" { | |
1329 | fn fcopyfile( | |
1330 | from: libc::c_int, | |
1331 | to: libc::c_int, | |
1332 | state: copyfile_state_t, | |
1333 | flags: copyfile_flags_t, | |
1334 | ) -> libc::c_int; | |
1335 | fn copyfile_state_alloc() -> copyfile_state_t; | |
1336 | fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int; | |
1337 | fn copyfile_state_get( | |
1338 | state: copyfile_state_t, | |
1339 | flag: u32, | |
1340 | dst: *mut libc::c_void, | |
1341 | ) -> libc::c_int; | |
1342 | } | |
1343 | ||
1344 | struct FreeOnDrop(copyfile_state_t); | |
1345 | impl Drop for FreeOnDrop { | |
1346 | fn drop(&mut self) { | |
1347 | // The code below ensures that `FreeOnDrop` is never a null pointer | |
1348 | unsafe { | |
1349 | // `copyfile_state_free` returns -1 if the `to` or `from` files | |
60c5eb7d | 1350 | // cannot be closed. However, this is not considered this an |
532ac7d7 XL |
1351 | // error. |
1352 | copyfile_state_free(self.0); | |
1353 | } | |
1354 | } | |
1355 | } | |
1356 | ||
48663c56 XL |
1357 | // MacOS prior to 10.12 don't support `fclonefileat` |
1358 | // We store the availability in a global to avoid unnecessary syscalls | |
1359 | static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true); | |
1360 | syscall! { | |
1361 | fn fclonefileat( | |
1362 | srcfd: libc::c_int, | |
1363 | dst_dirfd: libc::c_int, | |
136023e0 | 1364 | dst: *const c_char, |
48663c56 XL |
1365 | flags: libc::c_int |
1366 | ) -> libc::c_int | |
1367 | } | |
1368 | ||
1369 | let (reader, reader_metadata) = open_from(from)?; | |
1370 | ||
1371 | // Opportunistically attempt to create a copy-on-write clone of `from` | |
1372 | // using `fclonefileat`. | |
1373 | if HAS_FCLONEFILEAT.load(Ordering::Relaxed) { | |
1374 | let to = cstr(to)?; | |
dfeec247 XL |
1375 | let clonefile_result = |
1376 | cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }); | |
48663c56 XL |
1377 | match clonefile_result { |
1378 | Ok(_) => return Ok(reader_metadata.len()), | |
1379 | Err(err) => match err.raw_os_error() { | |
1380 | // `fclonefileat` will fail on non-APFS volumes, if the | |
1381 | // destination already exists, or if the source and destination | |
1382 | // are on different devices. In all these cases `fcopyfile` | |
1383 | // should succeed. | |
1384 | Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (), | |
1385 | Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed), | |
1386 | _ => return Err(err), | |
dfeec247 | 1387 | }, |
48663c56 XL |
1388 | } |
1389 | } | |
1390 | ||
1391 | // Fall back to using `fcopyfile` if `fclonefileat` does not succeed. | |
1392 | let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?; | |
532ac7d7 XL |
1393 | |
1394 | // We ensure that `FreeOnDrop` never contains a null pointer so it is | |
1395 | // always safe to call `copyfile_state_free` | |
1396 | let state = unsafe { | |
1397 | let state = copyfile_state_alloc(); | |
1398 | if state.is_null() { | |
1399 | return Err(crate::io::Error::last_os_error()); | |
1400 | } | |
1401 | FreeOnDrop(state) | |
1402 | }; | |
1403 | ||
dfeec247 | 1404 | let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA }; |
532ac7d7 | 1405 | |
dfeec247 | 1406 | cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?; |
532ac7d7 XL |
1407 | |
1408 | let mut bytes_copied: libc::off_t = 0; | |
1409 | cvt(unsafe { | |
1410 | copyfile_state_get( | |
1411 | state.0, | |
1412 | COPYFILE_STATE_COPIED, | |
1413 | &mut bytes_copied as *mut libc::off_t as *mut libc::c_void, | |
1414 | ) | |
1415 | })?; | |
1416 | Ok(bytes_copied as u64) | |
1417 | } | |
cdc7bbd5 | 1418 | |
17df50a5 | 1419 | #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] |
cdc7bbd5 XL |
1420 | pub fn chroot(dir: &Path) -> io::Result<()> { |
1421 | let dir = cstr(dir)?; | |
1422 | cvt(unsafe { libc::chroot(dir.as_ptr()) })?; | |
1423 | Ok(()) | |
1424 | } |