xref: /drstd/src/std/sys/unix/fs.rs (revision 0fe3ff0054d3aec7fbf9bddecfecb10bc7d23a51)
1 // miri has some special hacks here that make things unused.
2 #![cfg_attr(miri, allow(unused))]
3 
4 use crate::std::os::unix::prelude::*;
5 
6 use crate::std::ffi::{CStr, OsStr, OsString};
7 use crate::std::fmt;
8 use crate::std::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
9 use crate::std::mem;
10 use crate::std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
11 use crate::std::path::{Path, PathBuf};
12 use crate::std::ptr;
13 use crate::std::sync::Arc;
14 use crate::std::sys::common::small_c_string::run_path_with_cstr;
15 use crate::std::sys::fd::FileDesc;
16 use crate::std::sys::time::SystemTime;
17 use crate::std::sys::{cvt, cvt_r};
18 use crate::std::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
19 use dlibc;
20 
21 #[cfg(any(
22     all(target_os = "linux", target_env = "gnu"),
23     target_os = "macos",
24     target_os = "ios",
25     target_os = "tvos",
26     target_os = "watchos",
27 ))]
28 use crate::std::sys::weak::syscall;
29 #[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))]
30 use crate::std::sys::weak::weak;
31 
32 use dlibc::{c_int, mode_t};
33 
34 #[cfg(any(
35     target_os = "macos",
36     target_os = "ios",
37     target_os = "tvos",
38     target_os = "watchos",
39     target_os = "solaris",
40     target_os = "dragonos",
41     all(target_os = "linux", target_env = "gnu")
42 ))]
43 #[cfg(any(
44     target_os = "linux",
45     target_os = "emscripten",
46     target_os = "android",
47     target_os = "dragonos",
48 ))]
49 #[cfg(any(target_os = "linux", target_os = "emscripten",))]
50 use dlibc::fstatat64;
51 #[cfg(any(
52     target_os = "android",
53     target_os = "solaris",
54     target_os = "fuchsia",
55     target_os = "redox",
56     target_os = "illumos",
57     target_os = "nto",
58     target_os = "vita",
59 ))]
60 use dlibc::readdir as readdir64;
61 #[cfg(any(target_os = "linux", target_os = "dragonos",))]
62 use dlibc::readdir64;
63 #[cfg(any(target_os = "emscripten", target_os = "l4re"))]
64 use dlibc::readdir64_r;
65 #[cfg(not(any(
66     target_os = "android",
67     target_os = "linux",
68     target_os = "emscripten",
69     target_os = "solaris",
70     target_os = "illumos",
71     target_os = "l4re",
72     target_os = "fuchsia",
73     target_os = "redox",
74     target_os = "nto",
75     target_os = "vita",
76     target_os = "dragonos",
77 )))]
78 use dlibc::readdir_r as readdir64_r;
79 #[cfg(any(target_os = "android"))]
80 use dlibc::{
81     dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64,
82     lstat as lstat64, off64_t, open as open64, stat as stat64,
83 };
84 #[cfg(not(any(
85     target_os = "linux",
86     target_os = "emscripten",
87     target_os = "l4re",
88     target_os = "android",
89     target_os = "dragonos"
90 )))]
91 use dlibc::{
92     dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64,
93     lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
94 };
95 #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
96 use dlibc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64};
97 
98 #[cfg(any(target_os = "dragonos"))]
99 use dlibc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open as open64, stat64};
100 
101 pub use crate::std::sys_common::fs::try_exists;
102 
103 pub struct File(FileDesc);
104 
105 // FIXME: This should be available on Linux with all `target_env`.
106 // But currently only glibc exposes `statx` fn and structs.
107 // We don't want to import unverified raw C structs here directly.
108 // https://github.com/rust-lang/rust/pull/67774
109 macro_rules! cfg_has_statx {
110     ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
111         cfg_if::cfg_if! {
112             if #[cfg(all(target_os = "linux", target_env = "gnu",target_os = "dragonos",))] {
113                 $($then_tt)*
114             } else {
115                 $($else_tt)*
116             }
117         }
118     };
119     ($($block_inner:tt)*) => {
120         #[cfg(all(target_os = "linux", target_env = "gnu",target_os = "dragonos",))]
121         {
122             $($block_inner)*
123         }
124     };
125 }
126 
127 cfg_has_statx! {{
128     #[derive(Clone)]
129     pub struct FileAttr {
130         stat: stat64,
131         statx_extra_fields: Option<StatxExtraFields>,
132     }
133 
134     #[derive(Clone)]
135     struct StatxExtraFields {
136         // This is needed to check if btime is supported by the filesystem.
137         stx_mask: u32,
138         stx_btime: dlibc::statx_timestamp,
139         // With statx, we can overcome 32-bit `time_t` too.
140         #[cfg(target_pointer_width = "32")]
141         stx_atime: dlibc::statx_timestamp,
142         #[cfg(target_pointer_width = "32")]
143         stx_ctime: dlibc::statx_timestamp,
144         #[cfg(target_pointer_width = "32")]
145         stx_mtime: dlibc::statx_timestamp,
146 
147     }
148 
149     // We prefer `statx` on Linux if available, which contains file creation time,
150     // as well as 64-bit timestamps of all kinds.
151     // Default `stat64` contains no creation time and may have 32-bit `time_t`.
152     unsafe fn try_statx(
153         fd: c_int,
154         path: *const c_char,
155         flags: i32,
156         mask: u32,
157     ) -> Option<io::Result<FileAttr>> {
158         use crate::std::sync::atomic::{AtomicU8, Ordering};
159 
160         // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`.
161         // We check for it on first failure and remember availability to avoid having to
162         // do it again.
163         #[repr(u8)]
164         enum STATX_STATE{ Unknown = 0, Present, Unavailable }
165         static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8);
166 
167         syscall! {
168             fn statx(
169                 fd: c_int,
170                 pathname: *const c_char,
171                 flags: c_int,
172                 mask: dlibc::c_uint,
173                 statxbuf: *mut dlibc::statx
174             ) -> c_int
175         }
176 
177         if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 {
178             return None;
179         }
180 
181         let mut buf: dlibc::statx = mem::zeroed();
182         if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
183             if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 {
184                 return Some(Err(err));
185             }
186 
187             // Availability not checked yet.
188             //
189             // First try the cheap way.
190             if err.raw_os_error() == Some(dlibc::ENOSYS) {
191                 STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
192                 return None;
193             }
194 
195             // Error other than `ENOSYS` is not a good enough indicator -- it is
196             // known that `EPERM` can be returned as a result of using seccomp to
197             // block the syscall.
198             // Availability is checked by performing a call which expects `EFAULT`
199             // if the syscall is usable.
200             // See: https://github.com/rust-lang/rust/issues/65662
201             // FIXME this can probably just do the call if `EPERM` was received, but
202             // previous iteration of the code checked it for all errors and for now
203             // this is retained.
204             // FIXME what about transient conditions like `ENOMEM`?
205             let err2 = cvt(statx(0, ptr::null(), 0, dlibc::STATX_ALL, ptr::null_mut()))
206                 .err()
207                 .and_then(|e| e.raw_os_error());
208             if err2 == Some(dlibc::EFAULT) {
209                 STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
210                 return Some(Err(err));
211             } else {
212                 STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
213                 return None;
214             }
215         }
216 
217         // We cannot fill `stat64` exhaustively because of private padding fields.
218         let mut stat: stat64 = mem::zeroed();
219         // `c_ulong` on gnu-mips, `dev_t` otherwise
220         stat.st_dev = dlibc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
221         stat.st_ino = buf.stx_ino as dlibc::ino64_t;
222         stat.st_nlink = buf.stx_nlink as dlibc::nlink_t;
223         stat.st_mode = buf.stx_mode as dlibc::mode_t;
224         stat.st_uid = buf.stx_uid as dlibc::uid_t;
225         stat.st_gid = buf.stx_gid as dlibc::gid_t;
226         stat.st_rdev = dlibc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
227         stat.st_size = buf.stx_size as off64_t;
228         stat.st_blksize = buf.stx_blksize as dlibc::blksize_t;
229         stat.st_blocks = buf.stx_blocks as dlibc::blkcnt64_t;
230         stat.st_atime = buf.stx_atime.tv_sec as dlibc::time_t;
231         // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
232         stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
233         stat.st_mtime = buf.stx_mtime.tv_sec as dlibc::time_t;
234         stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
235         stat.st_ctime = buf.stx_ctime.tv_sec as dlibc::time_t;
236         stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
237 
238         let extra = StatxExtraFields {
239             stx_mask: buf.stx_mask,
240             stx_btime: buf.stx_btime,
241             // Store full times to avoid 32-bit `time_t` truncation.
242             #[cfg(target_pointer_width = "32")]
243             stx_atime: buf.stx_atime,
244             #[cfg(target_pointer_width = "32")]
245             stx_ctime: buf.stx_ctime,
246             #[cfg(target_pointer_width = "32")]
247             stx_mtime: buf.stx_mtime,
248         };
249 
250         Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
251     }
252 
253 } else {
254     #[derive(Clone)]
255     pub struct FileAttr {
256         stat: stat64,
257     }
258 }}
259 
260 // all DirEntry's will have a reference to this struct
261 struct InnerReadDir {
262     dirp: Dir,
263     root: PathBuf,
264 }
265 
266 pub struct ReadDir {
267     inner: Arc<InnerReadDir>,
268     end_of_stream: bool,
269 }
270 
271 impl ReadDir {
272     fn new(inner: InnerReadDir) -> Self {
273         Self {
274             inner: Arc::new(inner),
275             end_of_stream: false,
276         }
277     }
278 }
279 
280 struct Dir(*mut dlibc::DIR);
281 
282 unsafe impl Send for Dir {}
283 unsafe impl Sync for Dir {}
284 
285 #[cfg(any(
286     target_os = "android",
287     target_os = "linux",
288     target_os = "solaris",
289     target_os = "illumos",
290     target_os = "fuchsia",
291     target_os = "redox",
292     target_os = "nto",
293     target_os = "vita",
294     target_os = "dragonos",
295 ))]
296 pub struct DirEntry {
297     dir: Arc<InnerReadDir>,
298     entry: dirent64_min,
299     // We need to store an owned copy of the entry name on platforms that use
300     // readdir() (not readdir_r()), because a) struct dirent may use a flexible
301     // array to store the name, b) it lives only until the next readdir() call.
302     name: crate::std::ffi::CString,
303 }
304 
305 // Define a minimal subset of fields we need from `dirent64`, especially since
306 // we're not using the immediate `d_name` on these targets. Keeping this as an
307 // `entry` field in `DirEntry` helps reduce the `cfg` boilerplate elsewhere.
308 #[cfg(any(
309     target_os = "android",
310     target_os = "linux",
311     target_os = "solaris",
312     target_os = "illumos",
313     target_os = "fuchsia",
314     target_os = "redox",
315     target_os = "nto",
316     target_os = "vita",
317     target_os = "dragonos",
318 ))]
319 struct dirent64_min {
320     d_ino: u64,
321     #[cfg(not(any(
322         target_os = "solaris",
323         target_os = "illumos",
324         target_os = "nto",
325         target_os = "vita"
326     )))]
327     d_type: u8,
328 }
329 
330 #[cfg(not(any(
331     target_os = "android",
332     target_os = "linux",
333     target_os = "solaris",
334     target_os = "illumos",
335     target_os = "fuchsia",
336     target_os = "redox",
337     target_os = "nto",
338     target_os = "vita",
339     target_os = "dragonos",
340 )))]
341 pub struct DirEntry {
342     dir: Arc<InnerReadDir>,
343     // The full entry includes a fixed-length `d_name`.
344     entry: dirent64,
345 }
346 
347 #[derive(Clone, Debug)]
348 pub struct OpenOptions {
349     // generic
350     read: bool,
351     write: bool,
352     append: bool,
353     truncate: bool,
354     create: bool,
355     create_new: bool,
356     // system-specific
357     custom_flags: i32,
358     mode: mode_t,
359 }
360 
361 #[derive(Clone, PartialEq, Eq, Debug)]
362 pub struct FilePermissions {
363     mode: mode_t,
364 }
365 
366 #[derive(Copy, Clone, Debug, Default)]
367 pub struct FileTimes {
368     accessed: Option<SystemTime>,
369     modified: Option<SystemTime>,
370     #[cfg(any(
371         target_os = "macos",
372         target_os = "ios",
373         target_os = "watchos",
374         target_os = "tvos"
375     ))]
376     created: Option<SystemTime>,
377 }
378 
379 #[derive(Copy, Clone, Eq, Debug)]
380 pub struct FileType {
381     mode: mode_t,
382 }
383 
384 impl PartialEq for FileType {
385     fn eq(&self, other: &Self) -> bool {
386         self.masked() == other.masked()
387     }
388 }
389 
390 impl core::hash::Hash for FileType {
391     fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
392         self.masked().hash(state);
393     }
394 }
395 
396 #[derive(Debug)]
397 pub struct DirBuilder {
398     mode: mode_t,
399 }
400 
401 cfg_has_statx! {{
402     impl FileAttr {
403         fn from_stat64(stat: stat64) -> Self {
404             Self { stat, statx_extra_fields: None }
405         }
406 
407         #[cfg(target_pointer_width = "32")]
408         pub fn stx_mtime(&self) -> Option<&dlibc::statx_timestamp> {
409             if let Some(ext) = &self.statx_extra_fields {
410                 if (ext.stx_mask & dlibc::STATX_MTIME) != 0 {
411                     return Some(&ext.stx_mtime);
412                 }
413             }
414             None
415         }
416 
417         #[cfg(target_pointer_width = "32")]
418         pub fn stx_atime(&self) -> Option<&dlibc::statx_timestamp> {
419             if let Some(ext) = &self.statx_extra_fields {
420                 if (ext.stx_mask & dlibc::STATX_ATIME) != 0 {
421                     return Some(&ext.stx_atime);
422                 }
423             }
424             None
425         }
426 
427         #[cfg(target_pointer_width = "32")]
428         pub fn stx_ctime(&self) -> Option<&dlibc::statx_timestamp> {
429             if let Some(ext) = &self.statx_extra_fields {
430                 if (ext.stx_mask & dlibc::STATX_CTIME) != 0 {
431                     return Some(&ext.stx_ctime);
432                 }
433             }
434             None
435         }
436     }
437 } else {
438     impl FileAttr {
439         fn from_stat64(stat: stat64) -> Self {
440             Self { stat }
441         }
442     }
443 }}
444 
445 impl FileAttr {
446     pub fn size(&self) -> u64 {
447         self.stat.st_size as u64
448     }
449     pub fn perm(&self) -> FilePermissions {
450         FilePermissions {
451             mode: (self.stat.st_mode as mode_t),
452         }
453     }
454 
455     pub fn file_type(&self) -> FileType {
456         FileType {
457             mode: self.stat.st_mode as mode_t,
458         }
459     }
460 }
461 
462 #[cfg(target_os = "netbsd")]
463 impl FileAttr {
464     pub fn modified(&self) -> io::Result<SystemTime> {
465         Ok(SystemTime::new(
466             self.stat.st_mtime as i64,
467             self.stat.st_mtimensec as i64,
468         ))
469     }
470 
471     pub fn accessed(&self) -> io::Result<SystemTime> {
472         Ok(SystemTime::new(
473             self.stat.st_atime as i64,
474             self.stat.st_atimensec as i64,
475         ))
476     }
477 
478     pub fn created(&self) -> io::Result<SystemTime> {
479         Ok(SystemTime::new(
480             self.stat.st_birthtime as i64,
481             self.stat.st_birthtimensec as i64,
482         ))
483     }
484 }
485 
486 #[cfg(not(any(target_os = "netbsd", target_os = "nto")))]
487 impl FileAttr {
488     #[cfg(not(any(
489         target_os = "vxworks",
490         target_os = "espidf",
491         target_os = "horizon",
492         target_os = "vita"
493     )))]
494     pub fn modified(&self) -> io::Result<SystemTime> {
495         #[cfg(target_pointer_width = "32")]
496         cfg_has_statx! {
497             if let Some(mtime) = self.stx_mtime() {
498                 return Ok(SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64));
499             }
500         }
501 
502         Ok(SystemTime::new(
503             self.stat.st_mtime as i64,
504             self.stat.st_mtime_nsec as i64,
505         ))
506     }
507 
508     #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))]
509     pub fn modified(&self) -> io::Result<SystemTime> {
510         Ok(SystemTime::new(self.stat.st_mtime as i64, 0))
511     }
512 
513     #[cfg(target_os = "horizon")]
514     pub fn modified(&self) -> io::Result<SystemTime> {
515         Ok(SystemTime::from(self.stat.st_mtim))
516     }
517 
518     #[cfg(not(any(
519         target_os = "vxworks",
520         target_os = "espidf",
521         target_os = "horizon",
522         target_os = "vita"
523     )))]
524     pub fn accessed(&self) -> io::Result<SystemTime> {
525         #[cfg(target_pointer_width = "32")]
526         cfg_has_statx! {
527             if let Some(atime) = self.stx_atime() {
528                 return Ok(SystemTime::new(atime.tv_sec, atime.tv_nsec as i64));
529             }
530         }
531 
532         Ok(SystemTime::new(
533             self.stat.st_atime as i64,
534             self.stat.st_atime_nsec as i64,
535         ))
536     }
537 
538     #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))]
539     pub fn accessed(&self) -> io::Result<SystemTime> {
540         Ok(SystemTime::new(self.stat.st_atime as i64, 0))
541     }
542 
543     #[cfg(target_os = "horizon")]
544     pub fn accessed(&self) -> io::Result<SystemTime> {
545         Ok(SystemTime::from(self.stat.st_atim))
546     }
547 
548     #[cfg(any(
549         target_os = "freebsd",
550         target_os = "openbsd",
551         target_os = "macos",
552         target_os = "ios",
553         target_os = "tvos",
554         target_os = "watchos",
555     ))]
556     pub fn created(&self) -> io::Result<SystemTime> {
557         Ok(SystemTime::new(
558             self.stat.st_birthtime as i64,
559             self.stat.st_birthtime_nsec as i64,
560         ))
561     }
562 
563     #[cfg(not(any(
564         target_os = "freebsd",
565         target_os = "openbsd",
566         target_os = "macos",
567         target_os = "ios",
568         target_os = "tvos",
569         target_os = "watchos",
570         target_os = "vita",
571     )))]
572     pub fn created(&self) -> io::Result<SystemTime> {
573         cfg_has_statx! {
574             if let Some(ext) = &self.statx_extra_fields {
575                 return if (ext.stx_mask & dlibc::STATX_BTIME) != 0 {
576                     Ok(SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64))
577                 } else {
578                     Err(io::const_io_error!(
579                         io::ErrorKind::Uncategorized,
580                         "creation time is not available for the filesystem",
581                     ))
582                 };
583             }
584         }
585 
586         Err(io::const_io_error!(
587             io::ErrorKind::Unsupported,
588             "creation time is not available on this platform \
589                             currently",
590         ))
591     }
592 
593     #[cfg(target_os = "vita")]
594     pub fn created(&self) -> io::Result<SystemTime> {
595         Ok(SystemTime::new(self.stat.st_ctime as i64, 0))
596     }
597 }
598 
599 #[cfg(target_os = "nto")]
600 impl FileAttr {
601     pub fn modified(&self) -> io::Result<SystemTime> {
602         Ok(SystemTime::new(
603             self.stat.st_mtim.tv_sec,
604             self.stat.st_mtim.tv_nsec,
605         ))
606     }
607 
608     pub fn accessed(&self) -> io::Result<SystemTime> {
609         Ok(SystemTime::new(
610             self.stat.st_atim.tv_sec,
611             self.stat.st_atim.tv_nsec,
612         ))
613     }
614 
615     pub fn created(&self) -> io::Result<SystemTime> {
616         Ok(SystemTime::new(
617             self.stat.st_ctim.tv_sec,
618             self.stat.st_ctim.tv_nsec,
619         ))
620     }
621 }
622 
623 impl AsInner<stat64> for FileAttr {
624     #[inline]
625     fn as_inner(&self) -> &stat64 {
626         &self.stat
627     }
628 }
629 
630 impl FilePermissions {
631     pub fn readonly(&self) -> bool {
632         // check if any class (owner, group, others) has write permission
633         self.mode & 0o222 == 0
634     }
635 
636     pub fn set_readonly(&mut self, readonly: bool) {
637         if readonly {
638             // remove write permission for all classes; equivalent to `chmod a-w <file>`
639             self.mode &= !0o222;
640         } else {
641             // add write permission for all classes; equivalent to `chmod a+w <file>`
642             self.mode |= 0o222;
643         }
644     }
645     pub fn mode(&self) -> u32 {
646         self.mode as u32
647     }
648 }
649 
650 impl FileTimes {
651     pub fn set_accessed(&mut self, t: SystemTime) {
652         self.accessed = Some(t);
653     }
654 
655     pub fn set_modified(&mut self, t: SystemTime) {
656         self.modified = Some(t);
657     }
658 
659     #[cfg(any(
660         target_os = "macos",
661         target_os = "ios",
662         target_os = "watchos",
663         target_os = "tvos"
664     ))]
665     pub fn set_created(&mut self, t: SystemTime) {
666         self.created = Some(t);
667     }
668 }
669 
670 impl FileType {
671     pub fn is_dir(&self) -> bool {
672         self.is(dlibc::S_IFDIR)
673     }
674     pub fn is_file(&self) -> bool {
675         self.is(dlibc::S_IFREG)
676     }
677     pub fn is_symlink(&self) -> bool {
678         self.is(dlibc::S_IFLNK)
679     }
680 
681     pub fn is(&self, mode: mode_t) -> bool {
682         self.masked() == mode
683     }
684 
685     fn masked(&self) -> mode_t {
686         self.mode & dlibc::S_IFMT
687     }
688 }
689 
690 impl FromInner<u32> for FilePermissions {
691     fn from_inner(mode: u32) -> FilePermissions {
692         FilePermissions {
693             mode: mode as mode_t,
694         }
695     }
696 }
697 
698 impl fmt::Debug for ReadDir {
699     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
700         // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
701         // Thus the result will be e g 'ReadDir("/home")'
702         fmt::Debug::fmt(&*self.inner.root, f)
703     }
704 }
705 
706 impl Iterator for ReadDir {
707     type Item = io::Result<DirEntry>;
708 
709     #[cfg(any(
710         target_os = "android",
711         target_os = "linux",
712         target_os = "solaris",
713         target_os = "fuchsia",
714         target_os = "redox",
715         target_os = "illumos",
716         target_os = "nto",
717         target_os = "vita",
718         target_os = "dragonos",
719     ))]
720     fn next(&mut self) -> Option<io::Result<DirEntry>> {
721         if self.end_of_stream {
722             return None;
723         }
724 
725         unsafe {
726             loop {
727                 // As of POSIX.1-2017, readdir() is not required to be thread safe; only
728                 // readdir_r() is. However, readdir_r() cannot correctly handle platforms
729                 // with unlimited or variable NAME_MAX. Many modern platforms guarantee
730                 // thread safety for readdir() as long an individual DIR* is not accessed
731                 // concurrently, which is sufficient for Rust.
732                 super::os::set_errno(0);
733                 let entry_ptr = readdir64(self.inner.dirp.0);
734                 if entry_ptr.is_null() {
735                     // We either encountered an error, or reached the end. Either way,
736                     // the next call to next() should return None.
737                     self.end_of_stream = true;
738 
739                     // To distinguish between errors and end-of-directory, we had to clear
740                     // errno beforehand to check for an error now.
741                     return match super::os::errno() {
742                         0 => None,
743                         e => Some(Err(Error::from_raw_os_error(e))),
744                     };
745                 }
746 
747                 // The dirent64 struct is a weird imaginary thing that isn't ever supposed
748                 // to be worked with by value. Its trailing d_name field is declared
749                 // variously as [c_char; 256] or [c_char; 1] on different systems but
750                 // either way that size is meaningless; only the offset of d_name is
751                 // meaningful. The dirent64 pointers that libc returns from readdir64 are
752                 // allowed to point to allocations smaller _or_ LARGER than implied by the
753                 // definition of the struct.
754                 //
755                 // As such, we need to be even more careful with dirent64 than if its
756                 // contents were "simply" partially initialized data.
757                 //
758                 // Like for uninitialized contents, converting entry_ptr to `&dirent64`
759                 // would not be legal. However, unique to dirent64 is that we don't even
760                 // get to use `addr_of!((*entry_ptr).d_name)` because that operation
761                 // requires the full extent of *entry_ptr to be in bounds of the same
762                 // allocation, which is not necessarily the case here.
763                 //
764                 // Instead we must access fields individually through their offsets.
765                 macro_rules! offset_ptr {
766                     ($entry_ptr:expr, $field:ident) => {{
767                         const OFFSET: isize = mem::offset_of!(dirent64, $field) as isize;
768                         if true {
769                             // Cast to the same type determined by the else branch.
770                             $entry_ptr.byte_offset(OFFSET).cast::<_>()
771                         } else {
772                             #[allow(deref_nullptr)]
773                             {
774                                 ptr::addr_of!((*ptr::null::<dirent64>()).$field)
775                             }
776                         }
777                     }};
778                 }
779 
780                 // d_name is guaranteed to be null-terminated.
781                 let name = CStr::from_ptr(offset_ptr!(entry_ptr, d_name).cast());
782                 let name_bytes = name.to_bytes();
783                 if name_bytes == b"." || name_bytes == b".." {
784                     continue;
785                 }
786 
787                 #[cfg(not(target_os = "vita"))]
788                 let entry = dirent64_min {
789                     d_ino: *offset_ptr!(entry_ptr, d_ino) as u64,
790                     #[cfg(not(any(
791                         target_os = "solaris",
792                         target_os = "illumos",
793                         target_os = "nto",
794                     )))]
795                     d_type: *offset_ptr!(entry_ptr, d_type) as u8,
796                 };
797 
798                 #[cfg(target_os = "vita")]
799                 let entry = dirent64_min { d_ino: 0u64 };
800 
801                 return Some(Ok(DirEntry {
802                     entry,
803                     name: name.to_owned(),
804                     dir: Arc::clone(&self.inner),
805                 }));
806             }
807         }
808     }
809 
810     #[cfg(not(any(
811         target_os = "android",
812         target_os = "linux",
813         target_os = "solaris",
814         target_os = "fuchsia",
815         target_os = "redox",
816         target_os = "illumos",
817         target_os = "nto",
818         target_os = "vita",
819         target_os = "dragonos",
820     )))]
821     fn next(&mut self) -> Option<io::Result<DirEntry>> {
822         if self.end_of_stream {
823             return None;
824         }
825 
826         unsafe {
827             let mut ret = DirEntry {
828                 entry: mem::zeroed(),
829                 dir: Arc::clone(&self.inner),
830             };
831             let mut entry_ptr = ptr::null_mut();
832             loop {
833                 let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr);
834                 if err != 0 {
835                     if entry_ptr.is_null() {
836                         // We encountered an error (which will be returned in this iteration), but
837                         // we also reached the end of the directory stream. The `end_of_stream`
838                         // flag is enabled to make sure that we return `None` in the next iteration
839                         // (instead of looping forever)
840                         self.end_of_stream = true;
841                     }
842                     return Some(Err(Error::from_raw_os_error(err)));
843                 }
844                 if entry_ptr.is_null() {
845                     return None;
846                 }
847                 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
848                     return Some(Ok(ret));
849                 }
850             }
851         }
852     }
853 }
854 
855 impl Drop for Dir {
856     fn drop(&mut self) {
857         let r = unsafe { dlibc::closedir(self.0) };
858         assert!(
859             r == 0 || crate::std::io::Error::last_os_error().is_interrupted(),
860             "unexpected error during closedir: {:?}",
861             crate::std::io::Error::last_os_error()
862         );
863     }
864 }
865 
866 impl DirEntry {
867     pub fn path(&self) -> PathBuf {
868         self.dir.root.join(self.file_name_os_str())
869     }
870 
871     pub fn file_name(&self) -> OsString {
872         self.file_name_os_str().to_os_string()
873     }
874 
875     #[cfg(all(
876         any(target_os = "linux", target_os = "emscripten", target_os = "android",),
877         not(miri)
878     ))]
879     pub fn metadata(&self) -> io::Result<FileAttr> {
880         let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
881         let name = self.name_cstr().as_ptr();
882 
883         cfg_has_statx! {
884             if let Some(ret) = unsafe { try_statx(
885                 fd,
886                 name,
887                 dlibc::AT_SYMLINK_NOFOLLOW | dlibc::AT_STATX_SYNC_AS_STAT,
888                 dlibc::STATX_ALL,
889             ) } {
890                 return ret;
891             }
892         }
893 
894         let mut stat: stat64 = unsafe { mem::zeroed() };
895         cvt(unsafe { fstatat64(fd, name, &mut stat, dlibc::AT_SYMLINK_NOFOLLOW) })?;
896         Ok(FileAttr::from_stat64(stat))
897     }
898 
899     #[cfg(any(
900         not(any(target_os = "linux", target_os = "emscripten", target_os = "android",)),
901         miri
902     ))]
903     pub fn metadata(&self) -> io::Result<FileAttr> {
904         lstat(&self.path())
905     }
906 
907     #[cfg(any(
908         target_os = "solaris",
909         target_os = "illumos",
910         target_os = "haiku",
911         target_os = "vxworks",
912         target_os = "nto",
913         target_os = "vita",
914     ))]
915     pub fn file_type(&self) -> io::Result<FileType> {
916         self.metadata().map(|m| m.file_type())
917     }
918 
919     #[cfg(not(any(
920         target_os = "solaris",
921         target_os = "illumos",
922         target_os = "haiku",
923         target_os = "vxworks",
924         target_os = "nto",
925         target_os = "vita",
926     )))]
927     pub fn file_type(&self) -> io::Result<FileType> {
928         match self.entry.d_type {
929             dlibc::DT_CHR => Ok(FileType {
930                 mode: dlibc::S_IFCHR,
931             }),
932             dlibc::DT_FIFO => Ok(FileType {
933                 mode: dlibc::S_IFIFO,
934             }),
935             dlibc::DT_LNK => Ok(FileType {
936                 mode: dlibc::S_IFLNK,
937             }),
938             dlibc::DT_REG => Ok(FileType {
939                 mode: dlibc::S_IFREG,
940             }),
941             dlibc::DT_SOCK => Ok(FileType {
942                 mode: dlibc::S_IFSOCK,
943             }),
944             dlibc::DT_DIR => Ok(FileType {
945                 mode: dlibc::S_IFDIR,
946             }),
947             dlibc::DT_BLK => Ok(FileType {
948                 mode: dlibc::S_IFBLK,
949             }),
950             _ => self.metadata().map(|m| m.file_type()),
951         }
952     }
953 
954     #[cfg(any(
955         target_os = "macos",
956         target_os = "ios",
957         target_os = "tvos",
958         target_os = "watchos",
959         target_os = "linux",
960         target_os = "emscripten",
961         target_os = "android",
962         target_os = "solaris",
963         target_os = "illumos",
964         target_os = "haiku",
965         target_os = "l4re",
966         target_os = "fuchsia",
967         target_os = "redox",
968         target_os = "vxworks",
969         target_os = "espidf",
970         target_os = "horizon",
971         target_os = "vita",
972         target_os = "nto",
973         target_os = "dragonos",
974     ))]
975     pub fn ino(&self) -> u64 {
976         self.entry.d_ino as u64
977     }
978 
979     #[cfg(any(
980         target_os = "freebsd",
981         target_os = "openbsd",
982         target_os = "netbsd",
983         target_os = "dragonfly"
984     ))]
985     pub fn ino(&self) -> u64 {
986         self.entry.d_fileno as u64
987     }
988 
989     #[cfg(any(
990         target_os = "macos",
991         target_os = "ios",
992         target_os = "tvos",
993         target_os = "watchos",
994         target_os = "netbsd",
995         target_os = "openbsd",
996         target_os = "freebsd",
997         target_os = "dragonfly"
998     ))]
999     fn name_bytes(&self) -> &[u8] {
1000         use crate::std::slice;
1001         unsafe {
1002             slice::from_raw_parts(
1003                 self.entry.d_name.as_ptr() as *const u8,
1004                 self.entry.d_namlen as usize,
1005             )
1006         }
1007     }
1008     #[cfg(not(any(
1009         target_os = "macos",
1010         target_os = "ios",
1011         target_os = "tvos",
1012         target_os = "watchos",
1013         target_os = "netbsd",
1014         target_os = "openbsd",
1015         target_os = "freebsd",
1016         target_os = "dragonfly"
1017     )))]
1018     fn name_bytes(&self) -> &[u8] {
1019         self.name_cstr().to_bytes()
1020     }
1021 
1022     #[cfg(not(any(
1023         target_os = "android",
1024         target_os = "linux",
1025         target_os = "solaris",
1026         target_os = "illumos",
1027         target_os = "fuchsia",
1028         target_os = "redox",
1029         target_os = "nto",
1030         target_os = "vita",
1031         target_os = "dragonos",
1032     )))]
1033     fn name_cstr(&self) -> &CStr {
1034         unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
1035     }
1036     #[cfg(any(
1037         target_os = "android",
1038         target_os = "linux",
1039         target_os = "solaris",
1040         target_os = "illumos",
1041         target_os = "fuchsia",
1042         target_os = "redox",
1043         target_os = "nto",
1044         target_os = "vita",
1045         target_os = "dragonos",
1046     ))]
1047     fn name_cstr(&self) -> &CStr {
1048         &self.name
1049     }
1050 
1051     pub fn file_name_os_str(&self) -> &OsStr {
1052         OsStr::from_bytes(self.name_bytes())
1053     }
1054 }
1055 
1056 impl OpenOptions {
1057     pub fn new() -> OpenOptions {
1058         OpenOptions {
1059             // generic
1060             read: false,
1061             write: false,
1062             append: false,
1063             truncate: false,
1064             create: false,
1065             create_new: false,
1066             // system-specific
1067             custom_flags: 0,
1068             mode: 0o666,
1069         }
1070     }
1071 
1072     pub fn read(&mut self, read: bool) {
1073         self.read = read;
1074     }
1075     pub fn write(&mut self, write: bool) {
1076         self.write = write;
1077     }
1078     pub fn append(&mut self, append: bool) {
1079         self.append = append;
1080     }
1081     pub fn truncate(&mut self, truncate: bool) {
1082         self.truncate = truncate;
1083     }
1084     pub fn create(&mut self, create: bool) {
1085         self.create = create;
1086     }
1087     pub fn create_new(&mut self, create_new: bool) {
1088         self.create_new = create_new;
1089     }
1090 
1091     pub fn custom_flags(&mut self, flags: i32) {
1092         self.custom_flags = flags;
1093     }
1094     pub fn mode(&mut self, mode: u32) {
1095         self.mode = mode as mode_t;
1096     }
1097 
1098     fn get_access_mode(&self) -> io::Result<c_int> {
1099         match (self.read, self.write, self.append) {
1100             (true, false, false) => Ok(dlibc::O_RDONLY),
1101             (false, true, false) => Ok(dlibc::O_WRONLY),
1102             (true, true, false) => Ok(dlibc::O_RDWR),
1103             (false, _, true) => Ok(dlibc::O_WRONLY | dlibc::O_APPEND),
1104             (true, _, true) => Ok(dlibc::O_RDWR | dlibc::O_APPEND),
1105             (false, false, false) => Err(Error::from_raw_os_error(dlibc::EINVAL)),
1106         }
1107     }
1108 
1109     fn get_creation_mode(&self) -> io::Result<c_int> {
1110         match (self.write, self.append) {
1111             (true, false) => {}
1112             (false, false) => {
1113                 if self.truncate || self.create || self.create_new {
1114                     return Err(Error::from_raw_os_error(dlibc::EINVAL));
1115                 }
1116             }
1117             (_, true) => {
1118                 if self.truncate && !self.create_new {
1119                     return Err(Error::from_raw_os_error(dlibc::EINVAL));
1120                 }
1121             }
1122         }
1123 
1124         Ok(match (self.create, self.truncate, self.create_new) {
1125             (false, false, false) => 0,
1126             (true, false, false) => dlibc::O_CREAT,
1127             (false, true, false) => dlibc::O_TRUNC,
1128             (true, true, false) => dlibc::O_CREAT | dlibc::O_TRUNC,
1129             (_, _, true) => dlibc::O_CREAT | dlibc::O_EXCL,
1130         })
1131     }
1132 }
1133 
1134 impl File {
1135     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
1136         run_path_with_cstr(path, |path| File::open_c(path, opts))
1137     }
1138 
1139     pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
1140         let flags = dlibc::O_CLOEXEC
1141             | opts.get_access_mode()?
1142             | opts.get_creation_mode()?
1143             | (opts.custom_flags as c_int & !dlibc::O_ACCMODE);
1144         // The third argument of `open64` is documented to have type `mode_t`. On
1145         // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
1146         // However, since this is a variadic function, C integer promotion rules mean that on
1147         // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
1148         let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
1149         Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
1150     }
1151 
1152     pub fn file_attr(&self) -> io::Result<FileAttr> {
1153         let fd = self.as_raw_fd();
1154 
1155         cfg_has_statx! {
1156             if let Some(ret) = unsafe { try_statx(
1157                 fd,
1158                 b"\0" as *const _ as *const c_char,
1159                 dlibc::AT_EMPTY_PATH | dlibc::AT_STATX_SYNC_AS_STAT,
1160                 dlibc::STATX_ALL,
1161             ) } {
1162                 return ret;
1163             }
1164         }
1165 
1166         let mut stat: stat64 = unsafe { mem::zeroed() };
1167         cvt(unsafe { fstat64(fd, &mut stat) })?;
1168         Ok(FileAttr::from_stat64(stat))
1169     }
1170 
1171     pub fn fsync(&self) -> io::Result<()> {
1172         cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
1173         return Ok(());
1174 
1175         #[cfg(any(
1176             target_os = "macos",
1177             target_os = "ios",
1178             target_os = "tvos",
1179             target_os = "watchos",
1180         ))]
1181         unsafe fn os_fsync(fd: c_int) -> c_int {
1182             dlibc::fcntl(fd, dlibc::F_FULLFSYNC)
1183         }
1184         #[cfg(not(any(
1185             target_os = "macos",
1186             target_os = "ios",
1187             target_os = "tvos",
1188             target_os = "watchos",
1189         )))]
1190         unsafe fn os_fsync(fd: c_int) -> c_int {
1191             dlibc::fsync(fd)
1192         }
1193     }
1194 
1195     pub fn datasync(&self) -> io::Result<()> {
1196         cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
1197         return Ok(());
1198 
1199         #[cfg(any(
1200             target_os = "macos",
1201             target_os = "ios",
1202             target_os = "tvos",
1203             target_os = "watchos",
1204         ))]
1205         unsafe fn os_datasync(fd: c_int) -> c_int {
1206             dlibc::fcntl(fd, dlibc::F_FULLFSYNC)
1207         }
1208         #[cfg(any(
1209             target_os = "freebsd",
1210             target_os = "linux",
1211             target_os = "android",
1212             target_os = "netbsd",
1213             target_os = "openbsd",
1214             target_os = "nto",
1215             target_os = "dragonos",
1216         ))]
1217         unsafe fn os_datasync(fd: c_int) -> c_int {
1218             dlibc::fdatasync(fd)
1219         }
1220         #[cfg(not(any(
1221             target_os = "android",
1222             target_os = "freebsd",
1223             target_os = "ios",
1224             target_os = "tvos",
1225             target_os = "linux",
1226             target_os = "macos",
1227             target_os = "netbsd",
1228             target_os = "openbsd",
1229             target_os = "watchos",
1230             target_os = "nto",
1231             target_os = "dragonos",
1232         )))]
1233         unsafe fn os_datasync(fd: c_int) -> c_int {
1234             dlibc::fsync(fd)
1235         }
1236     }
1237 
1238     pub fn truncate(&self, size: u64) -> io::Result<()> {
1239         let size: off64_t = size
1240             .try_into()
1241             .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1242         cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
1243     }
1244 
1245     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
1246         self.0.read(buf)
1247     }
1248 
1249     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
1250         self.0.read_vectored(bufs)
1251     }
1252 
1253     #[inline]
1254     pub fn is_read_vectored(&self) -> bool {
1255         self.0.is_read_vectored()
1256     }
1257 
1258     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
1259         self.0.read_at(buf, offset)
1260     }
1261 
1262     pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
1263         self.0.read_buf(cursor)
1264     }
1265 
1266     pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
1267         self.0.read_vectored_at(bufs, offset)
1268     }
1269 
1270     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
1271         self.0.write(buf)
1272     }
1273 
1274     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
1275         self.0.write_vectored(bufs)
1276     }
1277 
1278     #[inline]
1279     pub fn is_write_vectored(&self) -> bool {
1280         self.0.is_write_vectored()
1281     }
1282 
1283     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
1284         self.0.write_at(buf, offset)
1285     }
1286 
1287     pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
1288         self.0.write_vectored_at(bufs, offset)
1289     }
1290 
1291     #[inline]
1292     pub fn flush(&self) -> io::Result<()> {
1293         Ok(())
1294     }
1295 
1296     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
1297         let (whence, pos) = match pos {
1298             // Casting to `i64` is fine, too large values will end up as
1299             // negative which will cause an error in `lseek64`.
1300             SeekFrom::Start(off) => (dlibc::SEEK_SET, off as i64),
1301             SeekFrom::End(off) => (dlibc::SEEK_END, off),
1302             SeekFrom::Current(off) => (dlibc::SEEK_CUR, off),
1303         };
1304         let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) })?;
1305         Ok(n as u64)
1306     }
1307 
1308     pub fn duplicate(&self) -> io::Result<File> {
1309         self.0.duplicate().map(File)
1310     }
1311 
1312     pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
1313         cvt_r(|| unsafe { dlibc::fchmod(self.as_raw_fd(), perm.mode) })?;
1314         Ok(())
1315     }
1316 
1317     pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
1318         #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))]
1319         let to_timespec = |time: Option<SystemTime>| {
1320             match time {
1321                 Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts),
1322                 Some(time) if time > crate::std::sys::time::UNIX_EPOCH => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too large to set as a file time")),
1323                 Some(_) => Err(io::const_io_error!(io::ErrorKind::InvalidInput, "timestamp is too small to set as a file time")),
1324                 None => Ok(dlibc::timespec { tv_sec: 0, tv_nsec: dlibc::UTIME_OMIT as _ }),
1325             }
1326         };
1327         cfg_if::cfg_if! {
1328             if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] {
1329                 // Redox doesn't appear to support `UTIME_OMIT`.
1330                 // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore
1331                 // the same as for Redox.
1332                 let _ = times;
1333                 Err(io::const_io_error!(
1334                     io::ErrorKind::Unsupported,
1335                     "setting file times not supported",
1336                 ))
1337             } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] {
1338                 let mut buf = [mem::MaybeUninit::<dlibc::timespec>::uninit(); 3];
1339                 let mut num_times = 0;
1340                 let mut attrlist: dlibc::attrlist = unsafe { mem::zeroed() };
1341                 attrlist.bitmapcount = dlibc::ATTR_BIT_MAP_COUNT;
1342                 if times.created.is_some() {
1343                     buf[num_times].write(to_timespec(times.created)?);
1344                     num_times += 1;
1345                     attrlist.commonattr |= dlibc::ATTR_CMN_CRTIME;
1346                 }
1347                 if times.modified.is_some() {
1348                     buf[num_times].write(to_timespec(times.modified)?);
1349                     num_times += 1;
1350                     attrlist.commonattr |= dlibc::ATTR_CMN_MODTIME;
1351                 }
1352                 if times.accessed.is_some() {
1353                     buf[num_times].write(to_timespec(times.accessed)?);
1354                     num_times += 1;
1355                     attrlist.commonattr |= dlibc::ATTR_CMN_ACCTIME;
1356                 }
1357                 cvt(unsafe { dlibc::fsetattrlist(
1358                     self.as_raw_fd(),
1359                     (&attrlist as *const dlibc::attrlist).cast::<dlibc::c_void>().cast_mut(),
1360                     buf.as_ptr().cast::<dlibc::c_void>().cast_mut(),
1361                     num_times * mem::size_of::<dlibc::timespec>(),
1362                     0
1363                 ) })?;
1364                 Ok(())
1365             } else if #[cfg(target_os = "android")] {
1366                 let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
1367                 // futimens requires Android API level 19
1368                 cvt(unsafe {
1369                     weak!(fn futimens(c_int, *const dlibc::timespec) -> c_int);
1370                     match futimens.get() {
1371                         Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()),
1372                         None => return Err(io::const_io_error!(
1373                             io::ErrorKind::Unsupported,
1374                             "setting file times requires Android API level >= 19",
1375                         )),
1376                     }
1377                 })?;
1378                 Ok(())
1379             } else {
1380                 #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
1381                 {
1382                     use crate::std::sys::{time::__timespec64, weak::weak};
1383 
1384                     // Added in glibc 2.34
1385                     weak!(fn __futimens64(dlibc::c_int, *const __timespec64) -> dlibc::c_int);
1386 
1387                     if let Some(futimens64) = __futimens64.get() {
1388                         let to_timespec = |time: Option<SystemTime>| time.map(|time| time.t.to_timespec64())
1389                             .unwrap_or(__timespec64::new(0, dlibc::UTIME_OMIT as _));
1390                         let times = [to_timespec(times.accessed), to_timespec(times.modified)];
1391                         cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) })?;
1392                         return Ok(());
1393                     }
1394                 }
1395                 let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
1396                 cvt(unsafe { dlibc::futimens(self.as_raw_fd(), times.as_ptr()) })?;
1397                 Ok(())
1398             }
1399         }
1400     }
1401 }
1402 
1403 impl DirBuilder {
1404     pub fn new() -> DirBuilder {
1405         DirBuilder { mode: 0o777 }
1406     }
1407 
1408     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
1409         run_path_with_cstr(p, |p| {
1410             cvt(unsafe { dlibc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())
1411         })
1412     }
1413 
1414     pub fn set_mode(&mut self, mode: u32) {
1415         self.mode = mode as mode_t;
1416     }
1417 }
1418 
1419 impl AsInner<FileDesc> for File {
1420     #[inline]
1421     fn as_inner(&self) -> &FileDesc {
1422         &self.0
1423     }
1424 }
1425 
1426 impl AsInnerMut<FileDesc> for File {
1427     #[inline]
1428     fn as_inner_mut(&mut self) -> &mut FileDesc {
1429         &mut self.0
1430     }
1431 }
1432 
1433 impl IntoInner<FileDesc> for File {
1434     fn into_inner(self) -> FileDesc {
1435         self.0
1436     }
1437 }
1438 
1439 impl FromInner<FileDesc> for File {
1440     fn from_inner(file_desc: FileDesc) -> Self {
1441         Self(file_desc)
1442     }
1443 }
1444 
1445 impl AsFd for File {
1446     fn as_fd(&self) -> BorrowedFd<'_> {
1447         self.0.as_fd()
1448     }
1449 }
1450 
1451 impl AsRawFd for File {
1452     #[inline]
1453     fn as_raw_fd(&self) -> RawFd {
1454         self.0.as_raw_fd()
1455     }
1456 }
1457 
1458 impl IntoRawFd for File {
1459     fn into_raw_fd(self) -> RawFd {
1460         self.0.into_raw_fd()
1461     }
1462 }
1463 
1464 impl FromRawFd for File {
1465     unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
1466         Self(FromRawFd::from_raw_fd(raw_fd))
1467     }
1468 }
1469 
1470 impl fmt::Debug for File {
1471     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1472         #[cfg(any(
1473             target_os = "linux",
1474             target_os = "netbsd",
1475             target_os = "illumos",
1476             target_os = "solaris"
1477         ))]
1478         fn get_path(fd: c_int) -> Option<PathBuf> {
1479             let mut p = PathBuf::from("/proc/self/fd");
1480             p.push(&fd.to_string());
1481             readlink(&p).ok()
1482         }
1483 
1484         #[cfg(target_os = "macos")]
1485         fn get_path(fd: c_int) -> Option<PathBuf> {
1486             // FIXME: The use of PATH_MAX is generally not encouraged, but it
1487             // is inevitable in this case because macOS defines `fcntl` with
1488             // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
1489             // alternatives. If a better method is invented, it should be used
1490             // instead.
1491             let mut buf = vec![0; dlibc::PATH_MAX as usize];
1492             let n = unsafe { dlibc::fcntl(fd, dlibc::F_GETPATH, buf.as_ptr()) };
1493             if n == -1 {
1494                 return None;
1495             }
1496             let l = buf.iter().position(|&c| c == 0).unwrap();
1497             buf.truncate(l as usize);
1498             buf.shrink_to_fit();
1499             Some(PathBuf::from(OsString::from_vec(buf)))
1500         }
1501 
1502         #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
1503         fn get_path(fd: c_int) -> Option<PathBuf> {
1504             let info = Box::<dlibc::kinfo_file>::new_zeroed();
1505             let mut info = unsafe { info.assume_init() };
1506             info.kf_structsize = mem::size_of::<dlibc::kinfo_file>() as dlibc::c_int;
1507             let n = unsafe { dlibc::fcntl(fd, dlibc::F_KINFO, &mut *info) };
1508             if n == -1 {
1509                 return None;
1510             }
1511             let buf = unsafe {
1512                 CStr::from_ptr(info.kf_path.as_mut_ptr())
1513                     .to_bytes()
1514                     .to_vec()
1515             };
1516             Some(PathBuf::from(OsString::from_vec(buf)))
1517         }
1518 
1519         #[cfg(target_os = "vxworks")]
1520         fn get_path(fd: c_int) -> Option<PathBuf> {
1521             let mut buf = vec![0; dlibc::PATH_MAX as usize];
1522             let n = unsafe { dlibc::ioctl(fd, dlibc::FIOGETNAME, buf.as_ptr()) };
1523             if n == -1 {
1524                 return None;
1525             }
1526             let l = buf.iter().position(|&c| c == 0).unwrap();
1527             buf.truncate(l as usize);
1528             Some(PathBuf::from(OsString::from_vec(buf)))
1529         }
1530 
1531         #[cfg(not(any(
1532             target_os = "linux",
1533             target_os = "macos",
1534             target_os = "vxworks",
1535             all(target_os = "freebsd", target_arch = "x86_64"),
1536             target_os = "netbsd",
1537             target_os = "illumos",
1538             target_os = "solaris"
1539         )))]
1540         fn get_path(_fd: c_int) -> Option<PathBuf> {
1541             // FIXME(#24570): implement this for other Unix platforms
1542             None
1543         }
1544 
1545         #[cfg(any(
1546             target_os = "linux",
1547             target_os = "macos",
1548             target_os = "freebsd",
1549             target_os = "netbsd",
1550             target_os = "openbsd",
1551             target_os = "vxworks"
1552         ))]
1553         fn get_mode(fd: c_int) -> Option<(bool, bool)> {
1554             let mode = unsafe { dlibc::fcntl(fd, dlibc::F_GETFL) };
1555             if mode == -1 {
1556                 return None;
1557             }
1558             match mode & dlibc::O_ACCMODE {
1559                 dlibc::O_RDONLY => Some((true, false)),
1560                 dlibc::O_RDWR => Some((true, true)),
1561                 dlibc::O_WRONLY => Some((false, true)),
1562                 _ => None,
1563             }
1564         }
1565 
1566         #[cfg(not(any(
1567             target_os = "linux",
1568             target_os = "macos",
1569             target_os = "freebsd",
1570             target_os = "netbsd",
1571             target_os = "openbsd",
1572             target_os = "vxworks"
1573         )))]
1574         fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
1575             // FIXME(#24570): implement this for other Unix platforms
1576             None
1577         }
1578 
1579         let fd = self.as_raw_fd();
1580         let mut b = f.debug_struct("File");
1581         b.field("fd", &fd);
1582         if let Some(path) = get_path(fd) {
1583             b.field("path", &path);
1584         }
1585         if let Some((read, write)) = get_mode(fd) {
1586             b.field("read", &read).field("write", &write);
1587         }
1588         b.finish()
1589     }
1590 }
1591 
1592 pub fn readdir(path: &Path) -> io::Result<ReadDir> {
1593     let ptr = run_path_with_cstr(path, |p| unsafe { Ok(dlibc::opendir(p.as_ptr())) })?;
1594     if ptr.is_null() {
1595         Err(Error::last_os_error())
1596     } else {
1597         let root = path.to_path_buf();
1598         let inner = InnerReadDir {
1599             dirp: Dir(ptr),
1600             root,
1601         };
1602         Ok(ReadDir::new(inner))
1603     }
1604 }
1605 
1606 pub fn unlink(p: &Path) -> io::Result<()> {
1607     run_path_with_cstr(p, |p| cvt(unsafe { dlibc::unlink(p.as_ptr()) }).map(|_| ()))
1608 }
1609 
1610 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
1611     run_path_with_cstr(old, |old| {
1612         run_path_with_cstr(new, |new| {
1613             cvt(unsafe { dlibc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ())
1614         })
1615     })
1616 }
1617 
1618 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
1619     run_path_with_cstr(p, |p| {
1620         cvt_r(|| unsafe { dlibc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())
1621     })
1622 }
1623 
1624 pub fn rmdir(p: &Path) -> io::Result<()> {
1625     run_path_with_cstr(p, |p| cvt(unsafe { dlibc::rmdir(p.as_ptr()) }).map(|_| ()))
1626 }
1627 
1628 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
1629     run_path_with_cstr(p, |c_path| {
1630         let p = c_path.as_ptr();
1631 
1632         let mut buf = Vec::with_capacity(256);
1633 
1634         loop {
1635             #[allow(unused_unsafe)]
1636             let buf_read =
1637                 cvt(unsafe { dlibc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })?
1638                     as usize;
1639 
1640             unsafe {
1641                 buf.set_len(buf_read);
1642             }
1643 
1644             if buf_read != buf.capacity() {
1645                 buf.shrink_to_fit();
1646 
1647                 return Ok(PathBuf::from(OsString::from_vec(buf)));
1648             }
1649 
1650             // Trigger the internal buffer resizing logic of `Vec` by requiring
1651             // more space than the current capacity. The length is guaranteed to be
1652             // the same as the capacity due to the if statement above.
1653             buf.reserve(1);
1654         }
1655     })
1656 }
1657 
1658 pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
1659     run_path_with_cstr(original, |original| {
1660         run_path_with_cstr(link, |link| {
1661             cvt(unsafe { dlibc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ())
1662         })
1663     })
1664 }
1665 
1666 pub fn link(original: &Path, link: &Path) -> io::Result<()> {
1667     run_path_with_cstr(original, |original| {
1668         run_path_with_cstr(link, |link| {
1669             cfg_if::cfg_if! {
1670                 if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] {
1671                     // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
1672                     // it implementation-defined whether `link` follows symlinks, so rely on the
1673                     // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
1674                     // Android has `linkat` on newer versions, but we happen to know `link`
1675                     // always has the correct behavior, so it's here as well.
1676                     cvt(unsafe { dlibc::link(original.as_ptr(), link.as_ptr()) })?;
1677                 } else if #[cfg(any(target_os = "macos", target_os = "solaris"))] {
1678                     // MacOS (<=10.9) and Solaris 10 lack support for linkat while newer
1679                     // versions have it. We want to use linkat if it is available, so we use weak!
1680                     // to check. `linkat` is preferable to `link` because it gives us a flag to
1681                     // specify how symlinks should be handled. We pass 0 as the flags argument,
1682                     // meaning it shouldn't follow symlinks.
1683                     weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
1684 
1685                     if let Some(f) = linkat.get() {
1686                         cvt(unsafe { f(dlibc::AT_FDCWD, original.as_ptr(), dlibc::AT_FDCWD, link.as_ptr(), 0) })?;
1687                     } else {
1688                         cvt(unsafe { dlibc::link(original.as_ptr(), link.as_ptr()) })?;
1689                     };
1690                 } else {
1691                     // Where we can, use `linkat` instead of `link`; see the comment above
1692                     // this one for details on why.
1693                     cvt(unsafe { dlibc::linkat(dlibc::AT_FDCWD, original.as_ptr(), dlibc::AT_FDCWD, link.as_ptr(), 0) })?;
1694                 }
1695             }
1696             Ok(())
1697         })
1698     })
1699 }
1700 
1701 pub fn stat(p: &Path) -> io::Result<FileAttr> {
1702     run_path_with_cstr(p, |p| {
1703         cfg_has_statx! {
1704             if let Some(ret) = unsafe { try_statx(
1705                 dlibc::AT_FDCWD,
1706                 p.as_ptr(),
1707                 dlibc::AT_STATX_SYNC_AS_STAT,
1708                 dlibc::STATX_ALL,
1709             ) } {
1710                 return ret;
1711             }
1712         }
1713 
1714         let mut stat: stat64 = unsafe { mem::zeroed() };
1715         cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
1716         Ok(FileAttr::from_stat64(stat))
1717     })
1718 }
1719 
1720 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
1721     run_path_with_cstr(p, |p| {
1722         cfg_has_statx! {
1723             if let Some(ret) = unsafe { try_statx(
1724                 dlibc::AT_FDCWD,
1725                 p.as_ptr(),
1726                 dlibc::AT_SYMLINK_NOFOLLOW | dlibc::AT_STATX_SYNC_AS_STAT,
1727                 dlibc::STATX_ALL,
1728             ) } {
1729                 return ret;
1730             }
1731         }
1732 
1733         let mut stat: stat64 = unsafe { mem::zeroed() };
1734         cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
1735         Ok(FileAttr::from_stat64(stat))
1736     })
1737 }
1738 
1739 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
1740     let r = run_path_with_cstr(p, |path| unsafe {
1741         Ok(dlibc::realpath(path.as_ptr(), ptr::null_mut()))
1742     })?;
1743     if r.is_null() {
1744         return Err(io::Error::last_os_error());
1745     }
1746     Ok(PathBuf::from(OsString::from_vec(unsafe {
1747         let buf = CStr::from_ptr(r).to_bytes().to_vec();
1748         dlibc::free(r as *mut _);
1749         buf
1750     })))
1751 }
1752 
1753 fn open_from(from: &Path) -> io::Result<(crate::std::fs::File, crate::std::fs::Metadata)> {
1754     use crate::std::fs::File;
1755     use crate::std::sys_common::fs::NOT_FILE_ERROR;
1756 
1757     let reader = File::open(from)?;
1758     let metadata = reader.metadata()?;
1759     if !metadata.is_file() {
1760         return Err(NOT_FILE_ERROR);
1761     }
1762     Ok((reader, metadata))
1763 }
1764 
1765 #[cfg(target_os = "espidf")]
1766 fn open_to_and_set_permissions(
1767     to: &Path,
1768     reader_metadata: crate::std::fs::Metadata,
1769 ) -> io::Result<(crate::std::fs::File, crate::std::fs::Metadata)> {
1770     use crate::std::fs::OpenOptions;
1771     let writer = OpenOptions::new().open(to)?;
1772     let writer_metadata = writer.metadata()?;
1773     Ok((writer, writer_metadata))
1774 }
1775 
1776 #[cfg(not(target_os = "espidf"))]
1777 fn open_to_and_set_permissions(
1778     to: &Path,
1779     reader_metadata: crate::std::fs::Metadata,
1780 ) -> io::Result<(crate::std::fs::File, crate::std::fs::Metadata)> {
1781     use crate::std::fs::OpenOptions;
1782     use crate::std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
1783 
1784     let perm = reader_metadata.permissions();
1785     let writer = OpenOptions::new()
1786         // create the file with the correct mode right away
1787         .mode(perm.mode())
1788         .write(true)
1789         .create(true)
1790         .truncate(true)
1791         .open(to)?;
1792     let writer_metadata = writer.metadata()?;
1793     // fchmod is broken on vita
1794     #[cfg(not(target_os = "vita"))]
1795     if writer_metadata.is_file() {
1796         // Set the correct file permissions, in case the file already existed.
1797         // Don't set the permissions on already existing non-files like
1798         // pipes/FIFOs or device nodes.
1799         writer.set_permissions(perm)?;
1800     }
1801     Ok((writer, writer_metadata))
1802 }
1803 
1804 #[cfg(not(any(
1805     target_os = "linux",
1806     target_os = "android",
1807     target_os = "macos",
1808     target_os = "ios",
1809     target_os = "tvos",
1810     target_os = "watchos",
1811     target_os = "dragonos",
1812 )))]
1813 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1814     let (mut reader, reader_metadata) = open_from(from)?;
1815     let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
1816 
1817     io::copy(&mut reader, &mut writer)
1818 }
1819 
1820 #[cfg(any(target_os = "linux", target_os = "android", target_os = "dragonos",))]
1821 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1822     let (mut reader, reader_metadata) = open_from(from)?;
1823     let max_len = u64::MAX;
1824     let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
1825 
1826     use super::kernel_copy::{copy_regular_files, CopyResult};
1827 
1828     match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
1829         CopyResult::Ended(bytes) => Ok(bytes),
1830         CopyResult::Error(e, _) => Err(e),
1831         CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) {
1832             Ok(bytes) => Ok(bytes + written),
1833             Err(e) => Err(e),
1834         },
1835     }
1836 }
1837 
1838 #[cfg(any(
1839     target_os = "macos",
1840     target_os = "ios",
1841     target_os = "tvos",
1842     target_os = "watchos"
1843 ))]
1844 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1845     use crate::std::sync::atomic::{AtomicBool, Ordering};
1846 
1847     const COPYFILE_ACL: u32 = 1 << 0;
1848     const COPYFILE_STAT: u32 = 1 << 1;
1849     const COPYFILE_XATTR: u32 = 1 << 2;
1850     const COPYFILE_DATA: u32 = 1 << 3;
1851 
1852     const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL;
1853     const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR;
1854     const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA;
1855 
1856     const COPYFILE_STATE_COPIED: u32 = 8;
1857 
1858     #[allow(non_camel_case_types)]
1859     type copyfile_state_t = *mut dlibc::c_void;
1860     #[allow(non_camel_case_types)]
1861     type copyfile_flags_t = u32;
1862 
1863     extern "C" {
1864         fn fcopyfile(
1865             from: dlibc::c_int,
1866             to: dlibc::c_int,
1867             state: copyfile_state_t,
1868             flags: copyfile_flags_t,
1869         ) -> dlibc::c_int;
1870         fn copyfile_state_alloc() -> copyfile_state_t;
1871         fn copyfile_state_free(state: copyfile_state_t) -> dlibc::c_int;
1872         fn copyfile_state_get(
1873             state: copyfile_state_t,
1874             flag: u32,
1875             dst: *mut dlibc::c_void,
1876         ) -> dlibc::c_int;
1877     }
1878 
1879     struct FreeOnDrop(copyfile_state_t);
1880     impl Drop for FreeOnDrop {
1881         fn drop(&mut self) {
1882             // The code below ensures that `FreeOnDrop` is never a null pointer
1883             unsafe {
1884                 // `copyfile_state_free` returns -1 if the `to` or `from` files
1885                 // cannot be closed. However, this is not considered this an
1886                 // error.
1887                 copyfile_state_free(self.0);
1888             }
1889         }
1890     }
1891 
1892     // MacOS prior to 10.12 don't support `fclonefileat`
1893     // We store the availability in a global to avoid unnecessary syscalls
1894     static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true);
1895     syscall! {
1896         fn fclonefileat(
1897             srcfd: dlibc::c_int,
1898             dst_dirfd: dlibc::c_int,
1899             dst: *const c_char,
1900             flags: dlibc::c_int
1901         ) -> dlibc::c_int
1902     }
1903 
1904     let (reader, reader_metadata) = open_from(from)?;
1905 
1906     // Opportunistically attempt to create a copy-on-write clone of `from`
1907     // using `fclonefileat`.
1908     if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
1909         let clonefile_result = run_path_with_cstr(to, |to| {
1910             cvt(unsafe { fclonefileat(reader.as_raw_fd(), dlibc::AT_FDCWD, to.as_ptr(), 0) })
1911         });
1912         match clonefile_result {
1913             Ok(_) => return Ok(reader_metadata.len()),
1914             Err(err) => match err.raw_os_error() {
1915                 // `fclonefileat` will fail on non-APFS volumes, if the
1916                 // destination already exists, or if the source and destination
1917                 // are on different devices. In all these cases `fcopyfile`
1918                 // should succeed.
1919                 Some(dlibc::ENOTSUP) | Some(dlibc::EEXIST) | Some(dlibc::EXDEV) => (),
1920                 Some(dlibc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed),
1921                 _ => return Err(err),
1922             },
1923         }
1924     }
1925 
1926     // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
1927     let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?;
1928 
1929     // We ensure that `FreeOnDrop` never contains a null pointer so it is
1930     // always safe to call `copyfile_state_free`
1931     let state = unsafe {
1932         let state = copyfile_state_alloc();
1933         if state.is_null() {
1934             return Err(crate::std::io::Error::last_os_error());
1935         }
1936         FreeOnDrop(state)
1937     };
1938 
1939     let flags = if writer_metadata.is_file() {
1940         COPYFILE_ALL
1941     } else {
1942         COPYFILE_DATA
1943     };
1944 
1945     cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
1946 
1947     let mut bytes_copied: dlibc::off_t = 0;
1948     cvt(unsafe {
1949         copyfile_state_get(
1950             state.0,
1951             COPYFILE_STATE_COPIED,
1952             &mut bytes_copied as *mut dlibc::off_t as *mut dlibc::c_void,
1953         )
1954     })?;
1955     Ok(bytes_copied as u64)
1956 }
1957 
1958 pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
1959     run_path_with_cstr(path, |path| {
1960         cvt(unsafe { dlibc::chown(path.as_ptr(), uid as dlibc::uid_t, gid as dlibc::gid_t) })
1961             .map(|_| ())
1962     })
1963 }
1964 
1965 pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
1966     cvt(unsafe { dlibc::fchown(fd, uid as dlibc::uid_t, gid as dlibc::gid_t) })?;
1967     Ok(())
1968 }
1969 
1970 pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
1971     run_path_with_cstr(path, |path| {
1972         cvt(unsafe { dlibc::lchown(path.as_ptr(), uid as dlibc::uid_t, gid as dlibc::gid_t) })
1973             .map(|_| ())
1974     })
1975 }
1976 
1977 #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
1978 pub fn chroot(dir: &Path) -> io::Result<()> {
1979     run_path_with_cstr(dir, |dir| {
1980         cvt(unsafe { dlibc::chroot(dir.as_ptr()) }).map(|_| ())
1981     })
1982 }
1983 
1984 pub use remove_dir_impl::remove_dir_all;
1985 
1986 // Fallback for REDOX, ESP-ID, Horizon, Vita and Miri
1987 #[cfg(any(
1988     target_os = "redox",
1989     target_os = "espidf",
1990     target_os = "horizon",
1991     target_os = "vita",
1992     target_os = "nto",
1993     target_os = "dragonos",
1994     miri
1995 ))]
1996 mod remove_dir_impl {
1997     pub use crate::std::sys_common::fs::remove_dir_all;
1998 }
1999 
2000 // Modern implementation using openat(), unlinkat() and fdopendir()
2001 #[cfg(not(any(
2002     target_os = "redox",
2003     target_os = "espidf",
2004     target_os = "horizon",
2005     target_os = "vita",
2006     target_os = "nto",
2007     target_os = "dragonos",
2008     miri
2009 )))]
2010 mod remove_dir_impl {
2011     use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir};
2012     use crate::std::ffi::CStr;
2013     use crate::std::io;
2014     use crate::std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
2015     use crate::std::os::unix::prelude::{OwnedFd, RawFd};
2016     use crate::std::path::{Path, PathBuf};
2017     use crate::std::sys::common::small_c_string::run_path_with_cstr;
2018     use crate::std::sys::{cvt, cvt_r};
2019 
2020     #[cfg(not(any(
2021         all(target_os = "linux", target_env = "gnu"),
2022         all(target_os = "macos", not(target_arch = "aarch64")),
2023         all(target_os = "dragonos")
2024     )))]
2025     use dlibc::{fdopendir, openat, unlinkat};
2026     #[cfg(all(target_os = "linux", target_env = "gnu"))]
2027     use dlibc::{fdopendir, openat64 as openat, unlinkat};
2028     #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
2029     use macos_weak::{fdopendir, openat, unlinkat};
2030 
2031     #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
2032     mod macos_weak {
2033         use crate::std::sys::weak::weak;
2034         use dlibc::{c_char, c_int, DIR};
2035 
2036         fn get_openat_fn() -> Option<unsafe extern "C" fn(c_int, *const c_char, c_int) -> c_int> {
2037             weak!(fn openat(c_int, *const c_char, c_int) -> c_int);
2038             openat.get()
2039         }
2040 
2041         pub fn has_openat() -> bool {
2042             get_openat_fn().is_some()
2043         }
2044 
2045         pub unsafe fn openat(dirfd: c_int, pathname: *const c_char, flags: c_int) -> c_int {
2046             get_openat_fn()
2047                 .map(|openat| openat(dirfd, pathname, flags))
2048                 .unwrap_or_else(|| {
2049                     crate::std::sys::unix::os::set_errno(dlibc::ENOSYS);
2050                     -1
2051                 })
2052         }
2053 
2054         pub unsafe fn fdopendir(fd: c_int) -> *mut DIR {
2055             #[cfg(all(target_os = "macos", target_arch = "x86"))]
2056             weak!(fn fdopendir(c_int) -> *mut DIR, "fdopendir$INODE64$UNIX2003");
2057             #[cfg(all(target_os = "macos", target_arch = "x86_64"))]
2058             weak!(fn fdopendir(c_int) -> *mut DIR, "fdopendir$INODE64");
2059             fdopendir
2060                 .get()
2061                 .map(|fdopendir| fdopendir(fd))
2062                 .unwrap_or_else(|| {
2063                     crate::std::sys::unix::os::set_errno(dlibc::ENOSYS);
2064                     crate::std::ptr::null_mut()
2065                 })
2066         }
2067 
2068         pub unsafe fn unlinkat(dirfd: c_int, pathname: *const c_char, flags: c_int) -> c_int {
2069             weak!(fn unlinkat(c_int, *const c_char, c_int) -> c_int);
2070             unlinkat
2071                 .get()
2072                 .map(|unlinkat| unlinkat(dirfd, pathname, flags))
2073                 .unwrap_or_else(|| {
2074                     crate::std::sys::unix::os::set_errno(dlibc::ENOSYS);
2075                     -1
2076                 })
2077         }
2078     }
2079 
2080     pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
2081         let fd = cvt_r(|| unsafe {
2082             openat(
2083                 parent_fd.unwrap_or(dlibc::AT_FDCWD),
2084                 p.as_ptr(),
2085                 dlibc::O_CLOEXEC | dlibc::O_RDONLY | dlibc::O_NOFOLLOW | dlibc::O_DIRECTORY,
2086             )
2087         })?;
2088         Ok(unsafe { OwnedFd::from_raw_fd(fd) })
2089     }
2090 
2091     fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
2092         let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) };
2093         if ptr.is_null() {
2094             return Err(io::Error::last_os_error());
2095         }
2096         let dirp = Dir(ptr);
2097         // file descriptor is automatically closed by dlibc::closedir() now, so give up ownership
2098         let new_parent_fd = dir_fd.into_raw_fd();
2099         // a valid root is not needed because we do not call any functions involving the full path
2100         // of the `DirEntry`s.
2101         let dummy_root = PathBuf::new();
2102         let inner = InnerReadDir {
2103             dirp,
2104             root: dummy_root,
2105         };
2106         Ok((ReadDir::new(inner), new_parent_fd))
2107     }
2108 
2109     #[cfg(any(
2110         target_os = "solaris",
2111         target_os = "illumos",
2112         target_os = "haiku",
2113         target_os = "vxworks",
2114     ))]
2115     fn is_dir(_ent: &DirEntry) -> Option<bool> {
2116         None
2117     }
2118 
2119     #[cfg(not(any(
2120         target_os = "solaris",
2121         target_os = "illumos",
2122         target_os = "haiku",
2123         target_os = "vxworks",
2124     )))]
2125     fn is_dir(ent: &DirEntry) -> Option<bool> {
2126         match ent.entry.d_type {
2127             dlibc::DT_UNKNOWN => None,
2128             dlibc::DT_DIR => Some(true),
2129             _ => Some(false),
2130         }
2131     }
2132 
2133     fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {
2134         // try opening as directory
2135         let fd = match openat_nofollow_dironly(parent_fd, &path) {
2136             Err(err) if matches!(err.raw_os_error(), Some(dlibc::ENOTDIR | dlibc::ELOOP)) => {
2137                 // not a directory - don't traverse further
2138                 // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR)
2139                 return match parent_fd {
2140                     // unlink...
2141                     Some(parent_fd) => {
2142                         cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }).map(drop)
2143                     }
2144                     // ...unless this was supposed to be the deletion root directory
2145                     None => Err(err),
2146                 };
2147             }
2148             result => result?,
2149         };
2150 
2151         // open the directory passing ownership of the fd
2152         let (dir, fd) = fdreaddir(fd)?;
2153         for child in dir {
2154             let child = child?;
2155             let child_name = child.name_cstr();
2156             match is_dir(&child) {
2157                 Some(true) => {
2158                     remove_dir_all_recursive(Some(fd), child_name)?;
2159                 }
2160                 Some(false) => {
2161                     cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?;
2162                 }
2163                 None => {
2164                     // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
2165                     // if the process has the appropriate privileges. This however can causing orphaned
2166                     // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
2167                     // into it first instead of trying to unlink() it.
2168                     remove_dir_all_recursive(Some(fd), child_name)?;
2169                 }
2170             }
2171         }
2172 
2173         // unlink the directory after removing its contents
2174         cvt(unsafe {
2175             unlinkat(
2176                 parent_fd.unwrap_or(dlibc::AT_FDCWD),
2177                 path.as_ptr(),
2178                 dlibc::AT_REMOVEDIR,
2179             )
2180         })?;
2181         Ok(())
2182     }
2183 
2184     fn remove_dir_all_modern(p: &Path) -> io::Result<()> {
2185         // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
2186         // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
2187         // into symlinks.
2188         let attr = lstat(p)?;
2189         if attr.file_type().is_symlink() {
2190             crate::std::fs::remove_file(p)
2191         } else {
2192             run_path_with_cstr(p, |p| remove_dir_all_recursive(None, &p))
2193         }
2194     }
2195 
2196     #[cfg(not(all(target_os = "macos", not(target_arch = "aarch64"))))]
2197     pub fn remove_dir_all(p: &Path) -> io::Result<()> {
2198         remove_dir_all_modern(p)
2199     }
2200 
2201     #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
2202     pub fn remove_dir_all(p: &Path) -> io::Result<()> {
2203         if macos_weak::has_openat() {
2204             // openat() is available with macOS 10.10+, just like unlinkat() and fdopendir()
2205             remove_dir_all_modern(p)
2206         } else {
2207             // fall back to classic implementation
2208             crate::std::sys_common::fs::remove_dir_all(p)
2209         }
2210     }
2211 }
2212