1 use crate::os::unix::prelude::*;
2 
3 use crate::ffi::{CStr, CString, OsStr, OsString};
4 use crate::fmt;
5 use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom};
6 use crate::mem;
7 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
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};
14 use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
15 
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 
25 use libc::{c_int, mode_t};
26 
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;
33 #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
34 use libc::dirfd;
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",
41     target_os = "illumos",
42     target_os = "l4re",
43     target_os = "fuchsia",
44     target_os = "redox"
45 )))]
46 use libc::readdir_r as readdir64_r;
47 #[cfg(target_os = "android")]
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 };
66 
67 pub use crate::sys_common::fs::try_exists;
68 
69 pub struct File(FileDesc);
70 
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
75 macro_rules! cfg_has_statx {
76     ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
77         cfg_if::cfg_if! {
78             if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
79                 $($then_tt)*
80             } else {
81                 $($else_tt)*
82             }
83         }
84     };
85     ($($block_inner:tt)*) => {
86         #[cfg(all(target_os = "linux", target_env = "gnu"))]
87         {
88             $($block_inner)*
89         }
90     };
91 }
92 
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,
111         path: *const c_char,
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,
126                 pathname: *const c_char,
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 => {
135                 // It is a trick to call `statx` with null pointers to check if the syscall
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
138                 // faster than a normal successful call.
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 
196 // all DirEntry's will have a reference to this struct
197 struct InnerReadDir {
198     dirp: Dir,
199     root: PathBuf,
200 }
201 
202 pub struct ReadDir {
203     inner: Arc<InnerReadDir>,
204     #[cfg(not(any(
205         target_os = "solaris",
206         target_os = "illumos",
207         target_os = "fuchsia",
208         target_os = "redox",
209     )))]
210     end_of_stream: bool,
211 }
212 
213 struct Dir(*mut libc::DIR);
214 
215 unsafe impl Send for Dir {}
216 unsafe impl Sync for Dir {}
217 
218 pub struct DirEntry {
219     entry: dirent64,
220     dir: Arc<InnerReadDir>,
221     // We need to store an owned copy of the entry name
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.
225     #[cfg(any(
226         target_os = "solaris",
227         target_os = "illumos",
228         target_os = "fuchsia",
229         target_os = "redox"
230     ))]
231     name: CString,
232 }
233 
234 #[derive(Clone, Debug)]
235 pub struct OpenOptions {
236     // generic
237     read: bool,
238     write: bool,
239     append: bool,
240     truncate: bool,
241     create: bool,
242     create_new: bool,
243     // system-specific
244     custom_flags: i32,
245     mode: mode_t,
246 }
247 
248 #[derive(Clone, PartialEq, Eq, Debug)]
249 pub struct FilePermissions {
250     mode: mode_t,
251 }
252 
253 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
254 pub struct FileType {
255     mode: mode_t,
256 }
257 
258 #[derive(Debug)]
259 pub struct DirBuilder {
260     mode: mode_t,
261 }
262 
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 
277 impl FileAttr {
size(&self) -> u64278     pub fn size(&self) -> u64 {
279         self.stat.st_size as u64
280     }
perm(&self) -> FilePermissions281     pub fn perm(&self) -> FilePermissions {
282         FilePermissions { mode: (self.stat.st_mode as mode_t) }
283     }
284 
file_type(&self) -> FileType285     pub fn file_type(&self) -> FileType {
286         FileType { mode: self.stat.st_mode as mode_t }
287     }
288 }
289 
290 #[cfg(target_os = "netbsd")]
291 impl FileAttr {
modified(&self) -> io::Result<SystemTime>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 
accessed(&self) -> io::Result<SystemTime>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 
created(&self) -> io::Result<SystemTime>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     }
312 }
313 
314 #[cfg(not(target_os = "netbsd"))]
315 impl FileAttr {
316     #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
modified(&self) -> io::Result<SystemTime>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,
320             tv_nsec: self.stat.st_mtime_nsec as _,
321         }))
322     }
323 
324     #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
modified(&self) -> io::Result<SystemTime>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 
332     #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
accessed(&self) -> io::Result<SystemTime>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,
336             tv_nsec: self.stat.st_atime_nsec as _,
337         }))
338     }
339 
340     #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
accessed(&self) -> io::Result<SystemTime>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 
348     #[cfg(any(
349         target_os = "freebsd",
350         target_os = "openbsd",
351         target_os = "macos",
352         target_os = "ios"
353     ))]
created(&self) -> io::Result<SystemTime>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 
361     #[cfg(not(any(
362         target_os = "freebsd",
363         target_os = "openbsd",
364         target_os = "macos",
365         target_os = "ios"
366     )))]
created(&self) -> io::Result<SystemTime>367     pub fn created(&self) -> io::Result<SystemTime> {
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 {
376                     Err(io::Error::new_const(
377                         io::ErrorKind::Uncategorized,
378                         &"creation time is not available for the filesystem",
379                     ))
380                 };
381             }
382         }
383 
384         Err(io::Error::new_const(
385             io::ErrorKind::Unsupported,
386             &"creation time is not available on this platform \
387                             currently",
388         ))
389     }
390 }
391 
392 impl AsInner<stat64> for FileAttr {
as_inner(&self) -> &stat64393     fn as_inner(&self) -> &stat64 {
394         &self.stat
395     }
396 }
397 
398 impl FilePermissions {
readonly(&self) -> bool399     pub fn readonly(&self) -> bool {
400         // check if any class (owner, group, others) has write permission
401         self.mode & 0o222 == 0
402     }
403 
set_readonly(&mut self, readonly: bool)404     pub fn set_readonly(&mut self, readonly: bool) {
405         if readonly {
406             // remove write permission for all classes; equivalent to `chmod a-w <file>`
407             self.mode &= !0o222;
408         } else {
409             // add write permission for all classes; equivalent to `chmod a+w <file>`
410             self.mode |= 0o222;
411         }
412     }
mode(&self) -> u32413     pub fn mode(&self) -> u32 {
414         self.mode as u32
415     }
416 }
417 
418 impl FileType {
is_dir(&self) -> bool419     pub fn is_dir(&self) -> bool {
420         self.is(libc::S_IFDIR)
421     }
is_file(&self) -> bool422     pub fn is_file(&self) -> bool {
423         self.is(libc::S_IFREG)
424     }
is_symlink(&self) -> bool425     pub fn is_symlink(&self) -> bool {
426         self.is(libc::S_IFLNK)
427     }
428 
is(&self, mode: mode_t) -> bool429     pub fn is(&self, mode: mode_t) -> bool {
430         self.mode & libc::S_IFMT == mode
431     }
432 }
433 
434 impl FromInner<u32> for FilePermissions {
from_inner(mode: u32) -> FilePermissions435     fn from_inner(mode: u32) -> FilePermissions {
436         FilePermissions { mode: mode as mode_t }
437     }
438 }
439 
440 impl fmt::Debug for ReadDir {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result441     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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")'
444         fmt::Debug::fmt(&*self.inner.root, f)
445     }
446 }
447 
448 impl Iterator for ReadDir {
449     type Item = io::Result<DirEntry>;
450 
451     #[cfg(any(
452         target_os = "solaris",
453         target_os = "fuchsia",
454         target_os = "redox",
455         target_os = "illumos"
456     ))]
next(&mut self) -> Option<io::Result<DirEntry>>457     fn next(&mut self) -> Option<io::Result<DirEntry>> {
458         unsafe {
459             loop {
460                 // Although readdir_r(3) would be a correct function to use here because
461                 // of the thread safety, on Illumos and Fuchsia the readdir(3C) function
462                 // is safe to use in threaded applications and it is generally preferred
463                 // over the readdir_r(3C) function.
464                 super::os::set_errno(0);
465                 let entry_ptr = libc::readdir(self.inner.dirp.0);
466                 if entry_ptr.is_null() {
467                     // null can mean either the end is reached or an error occurred.
468                     // So we had to clear errno beforehand to check for an error now.
469                     return match super::os::errno() {
470                         0 => None,
471                         e => Some(Err(Error::from_raw_os_error(e))),
472                     };
473                 }
474 
475                 let ret = DirEntry {
476                     entry: *entry_ptr,
477                     // d_name is guaranteed to be null-terminated.
478                     name: CStr::from_ptr((*entry_ptr).d_name.as_ptr()).to_owned(),
479                     dir: Arc::clone(&self.inner),
480                 };
481                 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
482                     return Some(Ok(ret));
483                 }
484             }
485         }
486     }
487 
488     #[cfg(not(any(
489         target_os = "solaris",
490         target_os = "fuchsia",
491         target_os = "redox",
492         target_os = "illumos"
493     )))]
next(&mut self) -> Option<io::Result<DirEntry>>494     fn next(&mut self) -> Option<io::Result<DirEntry>> {
495         if self.end_of_stream {
496             return None;
497         }
498 
499         unsafe {
500             let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
501             let mut entry_ptr = ptr::null_mut();
502             loop {
503                 let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr);
504                 if err != 0 {
505                     if entry_ptr.is_null() {
506                         // We encountered an error (which will be returned in this iteration), but
507                         // we also reached the end of the directory stream. The `end_of_stream`
508                         // flag is enabled to make sure that we return `None` in the next iteration
509                         // (instead of looping forever)
510                         self.end_of_stream = true;
511                     }
512                     return Some(Err(Error::from_raw_os_error(err)));
513                 }
514                 if entry_ptr.is_null() {
515                     return None;
516                 }
517                 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
518                     return Some(Ok(ret));
519                 }
520             }
521         }
522     }
523 }
524 
525 impl Drop for Dir {
drop(&mut self)526     fn drop(&mut self) {
527         let r = unsafe { libc::closedir(self.0) };
528         debug_assert_eq!(r, 0);
529     }
530 }
531 
532 impl DirEntry {
path(&self) -> PathBuf533     pub fn path(&self) -> PathBuf {
534         self.dir.root.join(OsStr::from_bytes(self.name_bytes()))
535     }
536 
file_name(&self) -> OsString537     pub fn file_name(&self) -> OsString {
538         OsStr::from_bytes(self.name_bytes()).to_os_string()
539     }
540 
541     #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
metadata(&self) -> io::Result<FileAttr>542     pub fn metadata(&self) -> io::Result<FileAttr> {
543         let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
544         let name = self.entry.d_name.as_ptr();
545 
546         cfg_has_statx! {
547             if let Some(ret) = unsafe { try_statx(
548                 fd,
549                 name,
550                 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
551                 libc::STATX_ALL,
552             ) } {
553                 return ret;
554             }
555         }
556 
557         let mut stat: stat64 = unsafe { mem::zeroed() };
558         cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
559         Ok(FileAttr::from_stat64(stat))
560     }
561 
562     #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
metadata(&self) -> io::Result<FileAttr>563     pub fn metadata(&self) -> io::Result<FileAttr> {
564         lstat(&self.path())
565     }
566 
567     #[cfg(any(
568         target_os = "solaris",
569         target_os = "illumos",
570         target_os = "haiku",
571         target_os = "vxworks"
572     ))]
file_type(&self) -> io::Result<FileType>573     pub fn file_type(&self) -> io::Result<FileType> {
574         lstat(&self.path()).map(|m| m.file_type())
575     }
576 
577     #[cfg(not(any(
578         target_os = "solaris",
579         target_os = "illumos",
580         target_os = "haiku",
581         target_os = "vxworks"
582     )))]
file_type(&self) -> io::Result<FileType>583     pub fn file_type(&self) -> io::Result<FileType> {
584         match self.entry.d_type {
585             libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
586             libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
587             libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
588             libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
589             libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
590             libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
591             libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
592             _ => lstat(&self.path()).map(|m| m.file_type()),
593         }
594     }
595 
596     #[cfg(any(
597         target_os = "macos",
598         target_os = "ios",
599         target_os = "linux",
600         target_os = "emscripten",
601         target_os = "android",
602         target_os = "solaris",
603         target_os = "illumos",
604         target_os = "haiku",
605         target_os = "l4re",
606         target_os = "fuchsia",
607         target_os = "redox",
608         target_os = "vxworks",
609         target_os = "espidf"
610     ))]
ino(&self) -> u64611     pub fn ino(&self) -> u64 {
612         self.entry.d_ino as u64
613     }
614 
615     #[cfg(any(
616         target_os = "freebsd",
617         target_os = "openbsd",
618         target_os = "netbsd",
619         target_os = "dragonfly"
620     ))]
ino(&self) -> u64621     pub fn ino(&self) -> u64 {
622         self.entry.d_fileno as u64
623     }
624 
625     #[cfg(any(
626         target_os = "macos",
627         target_os = "ios",
628         target_os = "netbsd",
629         target_os = "openbsd",
630         target_os = "freebsd",
631         target_os = "dragonfly"
632     ))]
name_bytes(&self) -> &[u8]633     fn name_bytes(&self) -> &[u8] {
634         use crate::slice;
635         unsafe {
636             slice::from_raw_parts(
637                 self.entry.d_name.as_ptr() as *const u8,
638                 self.entry.d_namlen as usize,
639             )
640         }
641     }
642     #[cfg(any(
643         target_os = "android",
644         target_os = "linux",
645         target_os = "emscripten",
646         target_os = "l4re",
647         target_os = "haiku",
648         target_os = "vxworks",
649         target_os = "espidf"
650     ))]
name_bytes(&self) -> &[u8]651     fn name_bytes(&self) -> &[u8] {
652         unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() }
653     }
654     #[cfg(any(
655         target_os = "solaris",
656         target_os = "illumos",
657         target_os = "fuchsia",
658         target_os = "redox"
659     ))]
name_bytes(&self) -> &[u8]660     fn name_bytes(&self) -> &[u8] {
661         self.name.as_bytes()
662     }
663 
664     #[cfg(not(any(
665         target_os = "solaris",
666         target_os = "illumos",
667         target_os = "fuchsia",
668         target_os = "redox"
669     )))]
name_cstr(&self) -> &CStr670     fn name_cstr(&self) -> &CStr {
671         unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
672     }
673     #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "fuchsia"))]
name_cstr(&self) -> &CStr674     fn name_cstr(&self) -> &CStr {
675         &self.name
676     }
677 
file_name_os_str(&self) -> &OsStr678     pub fn file_name_os_str(&self) -> &OsStr {
679         OsStr::from_bytes(self.name_bytes())
680     }
681 }
682 
683 impl OpenOptions {
new() -> OpenOptions684     pub fn new() -> OpenOptions {
685         OpenOptions {
686             // generic
687             read: false,
688             write: false,
689             append: false,
690             truncate: false,
691             create: false,
692             create_new: false,
693             // system-specific
694             custom_flags: 0,
695             mode: 0o666,
696         }
697     }
698 
read(&mut self, read: bool)699     pub fn read(&mut self, read: bool) {
700         self.read = read;
701     }
write(&mut self, write: bool)702     pub fn write(&mut self, write: bool) {
703         self.write = write;
704     }
append(&mut self, append: bool)705     pub fn append(&mut self, append: bool) {
706         self.append = append;
707     }
truncate(&mut self, truncate: bool)708     pub fn truncate(&mut self, truncate: bool) {
709         self.truncate = truncate;
710     }
create(&mut self, create: bool)711     pub fn create(&mut self, create: bool) {
712         self.create = create;
713     }
create_new(&mut self, create_new: bool)714     pub fn create_new(&mut self, create_new: bool) {
715         self.create_new = create_new;
716     }
717 
custom_flags(&mut self, flags: i32)718     pub fn custom_flags(&mut self, flags: i32) {
719         self.custom_flags = flags;
720     }
mode(&mut self, mode: u32)721     pub fn mode(&mut self, mode: u32) {
722         self.mode = mode as mode_t;
723     }
724 
get_access_mode(&self) -> io::Result<c_int>725     fn get_access_mode(&self) -> io::Result<c_int> {
726         match (self.read, self.write, self.append) {
727             (true, false, false) => Ok(libc::O_RDONLY),
728             (false, true, false) => Ok(libc::O_WRONLY),
729             (true, true, false) => Ok(libc::O_RDWR),
730             (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
731             (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
732             (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
733         }
734     }
735 
get_creation_mode(&self) -> io::Result<c_int>736     fn get_creation_mode(&self) -> io::Result<c_int> {
737         match (self.write, self.append) {
738             (true, false) => {}
739             (false, false) => {
740                 if self.truncate || self.create || self.create_new {
741                     return Err(Error::from_raw_os_error(libc::EINVAL));
742                 }
743             }
744             (_, true) => {
745                 if self.truncate && !self.create_new {
746                     return Err(Error::from_raw_os_error(libc::EINVAL));
747                 }
748             }
749         }
750 
751         Ok(match (self.create, self.truncate, self.create_new) {
752             (false, false, false) => 0,
753             (true, false, false) => libc::O_CREAT,
754             (false, true, false) => libc::O_TRUNC,
755             (true, true, false) => libc::O_CREAT | libc::O_TRUNC,
756             (_, _, true) => libc::O_CREAT | libc::O_EXCL,
757         })
758     }
759 }
760 
761 impl File {
open(path: &Path, opts: &OpenOptions) -> io::Result<File>762     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
763         let path = cstr(path)?;
764         File::open_c(&path, opts)
765     }
766 
open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File>767     pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
768         let flags = libc::O_CLOEXEC
769             | opts.get_access_mode()?
770             | opts.get_creation_mode()?
771             | (opts.custom_flags as c_int & !libc::O_ACCMODE);
772         // The third argument of `open64` is documented to have type `mode_t`. On
773         // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
774         // However, since this is a variadic function, C integer promotion rules mean that on
775         // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
776         let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
777         Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
778     }
779 
file_attr(&self) -> io::Result<FileAttr>780     pub fn file_attr(&self) -> io::Result<FileAttr> {
781         let fd = self.as_raw_fd();
782 
783         cfg_has_statx! {
784             if let Some(ret) = unsafe { try_statx(
785                 fd,
786                 b"\0" as *const _ as *const c_char,
787                 libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
788                 libc::STATX_ALL,
789             ) } {
790                 return ret;
791             }
792         }
793 
794         let mut stat: stat64 = unsafe { mem::zeroed() };
795         cvt(unsafe { fstat64(fd, &mut stat) })?;
796         Ok(FileAttr::from_stat64(stat))
797     }
798 
fsync(&self) -> io::Result<()>799     pub fn fsync(&self) -> io::Result<()> {
800         cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
801         return Ok(());
802 
803         #[cfg(any(target_os = "macos", target_os = "ios"))]
804         unsafe fn os_fsync(fd: c_int) -> c_int {
805             libc::fcntl(fd, libc::F_FULLFSYNC)
806         }
807         #[cfg(not(any(target_os = "macos", target_os = "ios")))]
808         unsafe fn os_fsync(fd: c_int) -> c_int {
809             libc::fsync(fd)
810         }
811     }
812 
datasync(&self) -> io::Result<()>813     pub fn datasync(&self) -> io::Result<()> {
814         cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
815         return Ok(());
816 
817         #[cfg(any(target_os = "macos", target_os = "ios"))]
818         unsafe fn os_datasync(fd: c_int) -> c_int {
819             libc::fcntl(fd, libc::F_FULLFSYNC)
820         }
821         #[cfg(any(
822             target_os = "freebsd",
823             target_os = "linux",
824             target_os = "android",
825             target_os = "netbsd",
826             target_os = "openbsd"
827         ))]
828         unsafe fn os_datasync(fd: c_int) -> c_int {
829             libc::fdatasync(fd)
830         }
831         #[cfg(not(any(
832             target_os = "android",
833             target_os = "freebsd",
834             target_os = "ios",
835             target_os = "linux",
836             target_os = "macos",
837             target_os = "netbsd",
838             target_os = "openbsd"
839         )))]
840         unsafe fn os_datasync(fd: c_int) -> c_int {
841             libc::fsync(fd)
842         }
843     }
844 
truncate(&self, size: u64) -> io::Result<()>845     pub fn truncate(&self, size: u64) -> io::Result<()> {
846         #[cfg(target_os = "android")]
847         return crate::sys::android::ftruncate64(self.as_raw_fd(), size);
848 
849         #[cfg(not(target_os = "android"))]
850         {
851             use crate::convert::TryInto;
852             let size: off64_t =
853                 size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
854             cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
855         }
856     }
857 
read(&self, buf: &mut [u8]) -> io::Result<usize>858     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
859         self.0.read(buf)
860     }
861 
read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize>862     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
863         self.0.read_vectored(bufs)
864     }
865 
866     #[inline]
is_read_vectored(&self) -> bool867     pub fn is_read_vectored(&self) -> bool {
868         self.0.is_read_vectored()
869     }
870 
read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>871     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
872         self.0.read_at(buf, offset)
873     }
874 
write(&self, buf: &[u8]) -> io::Result<usize>875     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
876         self.0.write(buf)
877     }
878 
write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize>879     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
880         self.0.write_vectored(bufs)
881     }
882 
883     #[inline]
is_write_vectored(&self) -> bool884     pub fn is_write_vectored(&self) -> bool {
885         self.0.is_write_vectored()
886     }
887 
write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>888     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
889         self.0.write_at(buf, offset)
890     }
891 
flush(&self) -> io::Result<()>892     pub fn flush(&self) -> io::Result<()> {
893         Ok(())
894     }
895 
seek(&self, pos: SeekFrom) -> io::Result<u64>896     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
897         let (whence, pos) = match pos {
898             // Casting to `i64` is fine, too large values will end up as
899             // negative which will cause an error in `lseek64`.
900             SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
901             SeekFrom::End(off) => (libc::SEEK_END, off),
902             SeekFrom::Current(off) => (libc::SEEK_CUR, off),
903         };
904         let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?;
905         Ok(n as u64)
906     }
907 
duplicate(&self) -> io::Result<File>908     pub fn duplicate(&self) -> io::Result<File> {
909         self.0.duplicate().map(File)
910     }
911 
set_permissions(&self, perm: FilePermissions) -> io::Result<()>912     pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
913         cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
914         Ok(())
915     }
916 }
917 
918 impl DirBuilder {
new() -> DirBuilder919     pub fn new() -> DirBuilder {
920         DirBuilder { mode: 0o777 }
921     }
922 
mkdir(&self, p: &Path) -> io::Result<()>923     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
924         let p = cstr(p)?;
925         cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
926         Ok(())
927     }
928 
set_mode(&mut self, mode: u32)929     pub fn set_mode(&mut self, mode: u32) {
930         self.mode = mode as mode_t;
931     }
932 }
933 
cstr(path: &Path) -> io::Result<CString>934 fn cstr(path: &Path) -> io::Result<CString> {
935     Ok(CString::new(path.as_os_str().as_bytes())?)
936 }
937 
938 impl AsInner<FileDesc> for File {
as_inner(&self) -> &FileDesc939     fn as_inner(&self) -> &FileDesc {
940         &self.0
941     }
942 }
943 
944 impl AsInnerMut<FileDesc> for File {
as_inner_mut(&mut self) -> &mut FileDesc945     fn as_inner_mut(&mut self) -> &mut FileDesc {
946         &mut self.0
947     }
948 }
949 
950 impl IntoInner<FileDesc> for File {
into_inner(self) -> FileDesc951     fn into_inner(self) -> FileDesc {
952         self.0
953     }
954 }
955 
956 impl FromInner<FileDesc> for File {
from_inner(file_desc: FileDesc) -> Self957     fn from_inner(file_desc: FileDesc) -> Self {
958         Self(file_desc)
959     }
960 }
961 
962 impl AsFd for File {
as_fd(&self) -> BorrowedFd<'_>963     fn as_fd(&self) -> BorrowedFd<'_> {
964         self.0.as_fd()
965     }
966 }
967 
968 impl AsRawFd for File {
as_raw_fd(&self) -> RawFd969     fn as_raw_fd(&self) -> RawFd {
970         self.0.as_raw_fd()
971     }
972 }
973 
974 impl IntoRawFd for File {
into_raw_fd(self) -> RawFd975     fn into_raw_fd(self) -> RawFd {
976         self.0.into_raw_fd()
977     }
978 }
979 
980 impl FromRawFd for File {
from_raw_fd(raw_fd: RawFd) -> Self981     unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
982         Self(FromRawFd::from_raw_fd(raw_fd))
983     }
984 }
985 
986 impl fmt::Debug for File {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result987     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
988         #[cfg(any(target_os = "linux", target_os = "netbsd"))]
989         fn get_path(fd: c_int) -> Option<PathBuf> {
990             let mut p = PathBuf::from("/proc/self/fd");
991             p.push(&fd.to_string());
992             readlink(&p).ok()
993         }
994 
995         #[cfg(target_os = "macos")]
996         fn get_path(fd: c_int) -> Option<PathBuf> {
997             // FIXME: The use of PATH_MAX is generally not encouraged, but it
998             // is inevitable in this case because macOS defines `fcntl` with
999             // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
1000             // alternatives. If a better method is invented, it should be used
1001             // instead.
1002             let mut buf = vec![0; libc::PATH_MAX as usize];
1003             let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
1004             if n == -1 {
1005                 return None;
1006             }
1007             let l = buf.iter().position(|&c| c == 0).unwrap();
1008             buf.truncate(l as usize);
1009             buf.shrink_to_fit();
1010             Some(PathBuf::from(OsString::from_vec(buf)))
1011         }
1012 
1013         #[cfg(target_os = "vxworks")]
1014         fn get_path(fd: c_int) -> Option<PathBuf> {
1015             let mut buf = vec![0; libc::PATH_MAX as usize];
1016             let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
1017             if n == -1 {
1018                 return None;
1019             }
1020             let l = buf.iter().position(|&c| c == 0).unwrap();
1021             buf.truncate(l as usize);
1022             Some(PathBuf::from(OsString::from_vec(buf)))
1023         }
1024 
1025         #[cfg(not(any(
1026             target_os = "linux",
1027             target_os = "macos",
1028             target_os = "vxworks",
1029             target_os = "netbsd"
1030         )))]
1031         fn get_path(_fd: c_int) -> Option<PathBuf> {
1032             // FIXME(#24570): implement this for other Unix platforms
1033             None
1034         }
1035 
1036         #[cfg(any(target_os = "linux", target_os = "macos", target_os = "vxworks"))]
1037         fn get_mode(fd: c_int) -> Option<(bool, bool)> {
1038             let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
1039             if mode == -1 {
1040                 return None;
1041             }
1042             match mode & libc::O_ACCMODE {
1043                 libc::O_RDONLY => Some((true, false)),
1044                 libc::O_RDWR => Some((true, true)),
1045                 libc::O_WRONLY => Some((false, true)),
1046                 _ => None,
1047             }
1048         }
1049 
1050         #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))]
1051         fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
1052             // FIXME(#24570): implement this for other Unix platforms
1053             None
1054         }
1055 
1056         let fd = self.as_raw_fd();
1057         let mut b = f.debug_struct("File");
1058         b.field("fd", &fd);
1059         if let Some(path) = get_path(fd) {
1060             b.field("path", &path);
1061         }
1062         if let Some((read, write)) = get_mode(fd) {
1063             b.field("read", &read).field("write", &write);
1064         }
1065         b.finish()
1066     }
1067 }
1068 
readdir(p: &Path) -> io::Result<ReadDir>1069 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
1070     let root = p.to_path_buf();
1071     let p = cstr(p)?;
1072     unsafe {
1073         let ptr = libc::opendir(p.as_ptr());
1074         if ptr.is_null() {
1075             Err(Error::last_os_error())
1076         } else {
1077             let inner = InnerReadDir { dirp: Dir(ptr), root };
1078             Ok(ReadDir {
1079                 inner: Arc::new(inner),
1080                 #[cfg(not(any(
1081                     target_os = "solaris",
1082                     target_os = "illumos",
1083                     target_os = "fuchsia",
1084                     target_os = "redox",
1085                 )))]
1086                 end_of_stream: false,
1087             })
1088         }
1089     }
1090 }
1091 
unlink(p: &Path) -> io::Result<()>1092 pub fn unlink(p: &Path) -> io::Result<()> {
1093     let p = cstr(p)?;
1094     cvt(unsafe { libc::unlink(p.as_ptr()) })?;
1095     Ok(())
1096 }
1097 
rename(old: &Path, new: &Path) -> io::Result<()>1098 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
1099     let old = cstr(old)?;
1100     let new = cstr(new)?;
1101     cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
1102     Ok(())
1103 }
1104 
set_perm(p: &Path, perm: FilePermissions) -> io::Result<()>1105 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
1106     let p = cstr(p)?;
1107     cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
1108     Ok(())
1109 }
1110 
rmdir(p: &Path) -> io::Result<()>1111 pub fn rmdir(p: &Path) -> io::Result<()> {
1112     let p = cstr(p)?;
1113     cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
1114     Ok(())
1115 }
1116 
readlink(p: &Path) -> io::Result<PathBuf>1117 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
1118     let c_path = cstr(p)?;
1119     let p = c_path.as_ptr();
1120 
1121     let mut buf = Vec::with_capacity(256);
1122 
1123     loop {
1124         let buf_read =
1125             cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
1126 
1127         unsafe {
1128             buf.set_len(buf_read);
1129         }
1130 
1131         if buf_read != buf.capacity() {
1132             buf.shrink_to_fit();
1133 
1134             return Ok(PathBuf::from(OsString::from_vec(buf)));
1135         }
1136 
1137         // Trigger the internal buffer resizing logic of `Vec` by requiring
1138         // more space than the current capacity. The length is guaranteed to be
1139         // the same as the capacity due to the if statement above.
1140         buf.reserve(1);
1141     }
1142 }
1143 
symlink(original: &Path, link: &Path) -> io::Result<()>1144 pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
1145     let original = cstr(original)?;
1146     let link = cstr(link)?;
1147     cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?;
1148     Ok(())
1149 }
1150 
link(original: &Path, link: &Path) -> io::Result<()>1151 pub fn link(original: &Path, link: &Path) -> io::Result<()> {
1152     let original = cstr(original)?;
1153     let link = cstr(link)?;
1154     cfg_if::cfg_if! {
1155         if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf"))] {
1156             // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
1157             // it implementation-defined whether `link` follows symlinks, so rely on the
1158             // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
1159             // Android has `linkat` on newer versions, but we happen to know `link`
1160             // always has the correct behavior, so it's here as well.
1161             cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1162         } else if #[cfg(target_os = "macos")] {
1163             // On MacOS, older versions (<=10.9) lack support for linkat while newer
1164             // versions have it. We want to use linkat if it is available, so we use weak!
1165             // to check. `linkat` is preferable to `link` ecause it gives us a flag to
1166             // specify how symlinks should be handled. We pass 0 as the flags argument,
1167             // meaning it shouldn't follow symlinks.
1168             weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
1169 
1170             if let Some(f) = linkat.get() {
1171                 cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
1172             } else {
1173                 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1174             };
1175         } else {
1176             // Where we can, use `linkat` instead of `link`; see the comment above
1177             // this one for details on why.
1178             cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
1179         }
1180     }
1181     Ok(())
1182 }
1183 
stat(p: &Path) -> io::Result<FileAttr>1184 pub fn stat(p: &Path) -> io::Result<FileAttr> {
1185     let p = cstr(p)?;
1186 
1187     cfg_has_statx! {
1188         if let Some(ret) = unsafe { try_statx(
1189             libc::AT_FDCWD,
1190             p.as_ptr(),
1191             libc::AT_STATX_SYNC_AS_STAT,
1192             libc::STATX_ALL,
1193         ) } {
1194             return ret;
1195         }
1196     }
1197 
1198     let mut stat: stat64 = unsafe { mem::zeroed() };
1199     cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
1200     Ok(FileAttr::from_stat64(stat))
1201 }
1202 
lstat(p: &Path) -> io::Result<FileAttr>1203 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
1204     let p = cstr(p)?;
1205 
1206     cfg_has_statx! {
1207         if let Some(ret) = unsafe { try_statx(
1208             libc::AT_FDCWD,
1209             p.as_ptr(),
1210             libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
1211             libc::STATX_ALL,
1212         ) } {
1213             return ret;
1214         }
1215     }
1216 
1217     let mut stat: stat64 = unsafe { mem::zeroed() };
1218     cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
1219     Ok(FileAttr::from_stat64(stat))
1220 }
1221 
canonicalize(p: &Path) -> io::Result<PathBuf>1222 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
1223     let path = CString::new(p.as_os_str().as_bytes())?;
1224     let buf;
1225     unsafe {
1226         let r = libc::realpath(path.as_ptr(), ptr::null_mut());
1227         if r.is_null() {
1228             return Err(io::Error::last_os_error());
1229         }
1230         buf = CStr::from_ptr(r).to_bytes().to_vec();
1231         libc::free(r as *mut _);
1232     }
1233     Ok(PathBuf::from(OsString::from_vec(buf)))
1234 }
1235 
open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)>1236 fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1237     use crate::fs::File;
1238     use crate::sys_common::fs::NOT_FILE_ERROR;
1239 
1240     let reader = File::open(from)?;
1241     let metadata = reader.metadata()?;
1242     if !metadata.is_file() {
1243         return Err(NOT_FILE_ERROR);
1244     }
1245     Ok((reader, metadata))
1246 }
1247 
1248 #[cfg(target_os = "espidf")]
open_to_and_set_permissions( to: &Path, reader_metadata: crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)>1249 fn open_to_and_set_permissions(
1250     to: &Path,
1251     reader_metadata: crate::fs::Metadata,
1252 ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1253     use crate::fs::OpenOptions;
1254     let writer = OpenOptions::new().open(to)?;
1255     let writer_metadata = writer.metadata()?;
1256     Ok((writer, writer_metadata))
1257 }
1258 
1259 #[cfg(not(target_os = "espidf"))]
open_to_and_set_permissions( to: &Path, reader_metadata: crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)>1260 fn open_to_and_set_permissions(
1261     to: &Path,
1262     reader_metadata: crate::fs::Metadata,
1263 ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1264     use crate::fs::OpenOptions;
1265     use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
1266 
1267     let perm = reader_metadata.permissions();
1268     let writer = OpenOptions::new()
1269         // create the file with the correct mode right away
1270         .mode(perm.mode())
1271         .write(true)
1272         .create(true)
1273         .truncate(true)
1274         .open(to)?;
1275     let writer_metadata = writer.metadata()?;
1276     if writer_metadata.is_file() {
1277         // Set the correct file permissions, in case the file already existed.
1278         // Don't set the permissions on already existing non-files like
1279         // pipes/FIFOs or device nodes.
1280         writer.set_permissions(perm)?;
1281     }
1282     Ok((writer, writer_metadata))
1283 }
1284 
1285 #[cfg(not(any(
1286     target_os = "linux",
1287     target_os = "android",
1288     target_os = "macos",
1289     target_os = "ios"
1290 )))]
copy(from: &Path, to: &Path) -> io::Result<u64>1291 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1292     let (mut reader, reader_metadata) = open_from(from)?;
1293     let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
1294 
1295     io::copy(&mut reader, &mut writer)
1296 }
1297 
1298 #[cfg(any(target_os = "linux", target_os = "android"))]
copy(from: &Path, to: &Path) -> io::Result<u64>1299 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1300     let (mut reader, reader_metadata) = open_from(from)?;
1301     let max_len = u64::MAX;
1302     let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
1303 
1304     use super::kernel_copy::{copy_regular_files, CopyResult};
1305 
1306     match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
1307         CopyResult::Ended(bytes) => Ok(bytes),
1308         CopyResult::Error(e, _) => Err(e),
1309         CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) {
1310             Ok(bytes) => Ok(bytes + written),
1311             Err(e) => Err(e),
1312         },
1313     }
1314 }
1315 
1316 #[cfg(any(target_os = "macos", target_os = "ios"))]
copy(from: &Path, to: &Path) -> io::Result<u64>1317 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1318     use crate::sync::atomic::{AtomicBool, Ordering};
1319 
1320     const COPYFILE_ACL: u32 = 1 << 0;
1321     const COPYFILE_STAT: u32 = 1 << 1;
1322     const COPYFILE_XATTR: u32 = 1 << 2;
1323     const COPYFILE_DATA: u32 = 1 << 3;
1324 
1325     const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL;
1326     const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR;
1327     const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA;
1328 
1329     const COPYFILE_STATE_COPIED: u32 = 8;
1330 
1331     #[allow(non_camel_case_types)]
1332     type copyfile_state_t = *mut libc::c_void;
1333     #[allow(non_camel_case_types)]
1334     type copyfile_flags_t = u32;
1335 
1336     extern "C" {
1337         fn fcopyfile(
1338             from: libc::c_int,
1339             to: libc::c_int,
1340             state: copyfile_state_t,
1341             flags: copyfile_flags_t,
1342         ) -> libc::c_int;
1343         fn copyfile_state_alloc() -> copyfile_state_t;
1344         fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int;
1345         fn copyfile_state_get(
1346             state: copyfile_state_t,
1347             flag: u32,
1348             dst: *mut libc::c_void,
1349         ) -> libc::c_int;
1350     }
1351 
1352     struct FreeOnDrop(copyfile_state_t);
1353     impl Drop for FreeOnDrop {
1354         fn drop(&mut self) {
1355             // The code below ensures that `FreeOnDrop` is never a null pointer
1356             unsafe {
1357                 // `copyfile_state_free` returns -1 if the `to` or `from` files
1358                 // cannot be closed. However, this is not considered this an
1359                 // error.
1360                 copyfile_state_free(self.0);
1361             }
1362         }
1363     }
1364 
1365     // MacOS prior to 10.12 don't support `fclonefileat`
1366     // We store the availability in a global to avoid unnecessary syscalls
1367     static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true);
1368     syscall! {
1369         fn fclonefileat(
1370             srcfd: libc::c_int,
1371             dst_dirfd: libc::c_int,
1372             dst: *const c_char,
1373             flags: libc::c_int
1374         ) -> libc::c_int
1375     }
1376 
1377     let (reader, reader_metadata) = open_from(from)?;
1378 
1379     // Opportunistically attempt to create a copy-on-write clone of `from`
1380     // using `fclonefileat`.
1381     if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
1382         let to = cstr(to)?;
1383         let clonefile_result =
1384             cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) });
1385         match clonefile_result {
1386             Ok(_) => return Ok(reader_metadata.len()),
1387             Err(err) => match err.raw_os_error() {
1388                 // `fclonefileat` will fail on non-APFS volumes, if the
1389                 // destination already exists, or if the source and destination
1390                 // are on different devices. In all these cases `fcopyfile`
1391                 // should succeed.
1392                 Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
1393                 Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed),
1394                 _ => return Err(err),
1395             },
1396         }
1397     }
1398 
1399     // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
1400     let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?;
1401 
1402     // We ensure that `FreeOnDrop` never contains a null pointer so it is
1403     // always safe to call `copyfile_state_free`
1404     let state = unsafe {
1405         let state = copyfile_state_alloc();
1406         if state.is_null() {
1407             return Err(crate::io::Error::last_os_error());
1408         }
1409         FreeOnDrop(state)
1410     };
1411 
1412     let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA };
1413 
1414     cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
1415 
1416     let mut bytes_copied: libc::off_t = 0;
1417     cvt(unsafe {
1418         copyfile_state_get(
1419             state.0,
1420             COPYFILE_STATE_COPIED,
1421             &mut bytes_copied as *mut libc::off_t as *mut libc::c_void,
1422         )
1423     })?;
1424     Ok(bytes_copied as u64)
1425 }
1426 
chown(path: &Path, uid: u32, gid: u32) -> io::Result<()>1427 pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
1428     let path = cstr(path)?;
1429     cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
1430     Ok(())
1431 }
1432 
fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()>1433 pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
1434     cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?;
1435     Ok(())
1436 }
1437 
lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()>1438 pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
1439     let path = cstr(path)?;
1440     cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
1441     Ok(())
1442 }
1443 
1444 #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
chroot(dir: &Path) -> io::Result<()>1445 pub fn chroot(dir: &Path) -> io::Result<()> {
1446     let dir = cstr(dir)?;
1447     cvt(unsafe { libc::chroot(dir.as_ptr()) })?;
1448     Ok(())
1449 }
1450 
1451 pub use remove_dir_impl::remove_dir_all;
1452 
1453 // Fallback for REDOX
1454 #[cfg(target_os = "redox")]
1455 mod remove_dir_impl {
1456     pub use crate::sys_common::fs::remove_dir_all;
1457 }
1458 
1459 // Dynamically choose implementation Macos x86-64: modern for 10.10+, fallback for older versions
1460 #[cfg(all(target_os = "macos", target_arch = "x86_64"))]
1461 mod remove_dir_impl {
1462     use super::{cstr, lstat, Dir, InnerReadDir, ReadDir};
1463     use crate::ffi::CStr;
1464     use crate::io;
1465     use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
1466     use crate::os::unix::prelude::{OwnedFd, RawFd};
1467     use crate::path::{Path, PathBuf};
1468     use crate::sync::Arc;
1469     use crate::sys::weak::weak;
1470     use crate::sys::{cvt, cvt_r};
1471     use libc::{c_char, c_int, DIR};
1472 
openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd>1473     pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
1474         weak!(fn openat(c_int, *const c_char, c_int) -> c_int);
1475         let fd = cvt_r(|| unsafe {
1476             openat.get().unwrap()(
1477                 parent_fd.unwrap_or(libc::AT_FDCWD),
1478                 p.as_ptr(),
1479                 libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
1480             )
1481         })?;
1482         Ok(unsafe { OwnedFd::from_raw_fd(fd) })
1483     }
1484 
fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)>1485     fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
1486         weak!(fn fdopendir(c_int) -> *mut DIR, "fdopendir$INODE64");
1487         let ptr = unsafe { fdopendir.get().unwrap()(dir_fd.as_raw_fd()) };
1488         if ptr.is_null() {
1489             return Err(io::Error::last_os_error());
1490         }
1491         let dirp = Dir(ptr);
1492         // file descriptor is automatically closed by libc::closedir() now, so give up ownership
1493         let new_parent_fd = dir_fd.into_raw_fd();
1494         // a valid root is not needed because we do not call any functions involving the full path
1495         // of the DirEntrys.
1496         let dummy_root = PathBuf::new();
1497         Ok((
1498             ReadDir {
1499                 inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
1500                 end_of_stream: false,
1501             },
1502             new_parent_fd,
1503         ))
1504     }
1505 
remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()>1506     fn remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()> {
1507         weak!(fn unlinkat(c_int, *const c_char, c_int) -> c_int);
1508 
1509         let pcstr = cstr(p)?;
1510 
1511         // entry is expected to be a directory, open as such
1512         let fd = openat_nofollow_dironly(parent_fd, &pcstr)?;
1513 
1514         // open the directory passing ownership of the fd
1515         let (dir, fd) = fdreaddir(fd)?;
1516         for child in dir {
1517             let child = child?;
1518             match child.entry.d_type {
1519                 libc::DT_DIR => {
1520                     remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1521                 }
1522                 libc::DT_UNKNOWN => {
1523                     match cvt(unsafe { unlinkat.get().unwrap()(fd, child.name_cstr().as_ptr(), 0) })
1524                     {
1525                         // type unknown - try to unlink
1526                         Err(err) if err.raw_os_error() == Some(libc::EPERM) => {
1527                             // if the file is a directory unlink fails with EPERM
1528                             remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1529                         }
1530                         result => {
1531                             result?;
1532                         }
1533                     }
1534                 }
1535                 _ => {
1536                     // not a directory -> unlink
1537                     cvt(unsafe { unlinkat.get().unwrap()(fd, child.name_cstr().as_ptr(), 0) })?;
1538                 }
1539             }
1540         }
1541 
1542         // unlink the directory after removing its contents
1543         cvt(unsafe {
1544             unlinkat.get().unwrap()(
1545                 parent_fd.unwrap_or(libc::AT_FDCWD),
1546                 pcstr.as_ptr(),
1547                 libc::AT_REMOVEDIR,
1548             )
1549         })?;
1550         Ok(())
1551     }
1552 
remove_dir_all_modern(p: &Path) -> io::Result<()>1553     fn remove_dir_all_modern(p: &Path) -> io::Result<()> {
1554         // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
1555         // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
1556         // into symlinks.
1557         let attr = lstat(p)?;
1558         if attr.file_type().is_symlink() {
1559             crate::fs::remove_file(p)
1560         } else {
1561             remove_dir_all_recursive(None, p)
1562         }
1563     }
1564 
remove_dir_all(p: &Path) -> io::Result<()>1565     pub fn remove_dir_all(p: &Path) -> io::Result<()> {
1566         weak!(fn openat(c_int, *const c_char, c_int) -> c_int);
1567         if openat.get().is_some() {
1568             // openat() is available with macOS 10.10+, just like unlinkat() and fdopendir()
1569             remove_dir_all_modern(p)
1570         } else {
1571             // fall back to classic implementation
1572             crate::sys_common::fs::remove_dir_all(p)
1573         }
1574     }
1575 }
1576 
1577 // Modern implementation using openat(), unlinkat() and fdopendir()
1578 #[cfg(not(any(all(target_os = "macos", target_arch = "x86_64"), target_os = "redox")))]
1579 mod remove_dir_impl {
1580     use super::{cstr, lstat, Dir, DirEntry, InnerReadDir, ReadDir};
1581     use crate::ffi::CStr;
1582     use crate::io;
1583     use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
1584     use crate::os::unix::prelude::{OwnedFd, RawFd};
1585     use crate::path::{Path, PathBuf};
1586     use crate::sync::Arc;
1587     use crate::sys::{cvt, cvt_r};
1588     use libc::{fdopendir, openat, unlinkat};
1589 
openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd>1590     pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
1591         let fd = cvt_r(|| unsafe {
1592             openat(
1593                 parent_fd.unwrap_or(libc::AT_FDCWD),
1594                 p.as_ptr(),
1595                 libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
1596             )
1597         })?;
1598         Ok(unsafe { OwnedFd::from_raw_fd(fd) })
1599     }
1600 
fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)>1601     fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
1602         let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) };
1603         if ptr.is_null() {
1604             return Err(io::Error::last_os_error());
1605         }
1606         let dirp = Dir(ptr);
1607         // file descriptor is automatically closed by libc::closedir() now, so give up ownership
1608         let new_parent_fd = dir_fd.into_raw_fd();
1609         // a valid root is not needed because we do not call any functions involving the full path
1610         // of the DirEntrys.
1611         let dummy_root = PathBuf::new();
1612         Ok((
1613             ReadDir {
1614                 inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
1615                 #[cfg(not(any(
1616                     target_os = "solaris",
1617                     target_os = "illumos",
1618                     target_os = "fuchsia",
1619                     target_os = "redox",
1620                 )))]
1621                 end_of_stream: false,
1622             },
1623             new_parent_fd,
1624         ))
1625     }
1626 
1627     #[cfg(any(
1628         target_os = "solaris",
1629         target_os = "illumos",
1630         target_os = "haiku",
1631         target_os = "vxworks",
1632         target_os = "fuchsia"
1633     ))]
is_dir(_ent: &DirEntry) -> Option<bool>1634     fn is_dir(_ent: &DirEntry) -> Option<bool> {
1635         None
1636     }
1637 
1638     #[cfg(not(any(
1639         target_os = "solaris",
1640         target_os = "illumos",
1641         target_os = "haiku",
1642         target_os = "vxworks",
1643         target_os = "fuchsia"
1644     )))]
is_dir(ent: &DirEntry) -> Option<bool>1645     fn is_dir(ent: &DirEntry) -> Option<bool> {
1646         match ent.entry.d_type {
1647             libc::DT_UNKNOWN => None,
1648             libc::DT_DIR => Some(true),
1649             _ => Some(false),
1650         }
1651     }
1652 
remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()>1653     fn remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()> {
1654         let pcstr = cstr(p)?;
1655 
1656         // entry is expected to be a directory, open as such
1657         let fd = openat_nofollow_dironly(parent_fd, &pcstr)?;
1658 
1659         // open the directory passing ownership of the fd
1660         let (dir, fd) = fdreaddir(fd)?;
1661         for child in dir {
1662             let child = child?;
1663             match is_dir(&child) {
1664                 Some(true) => {
1665                     remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1666                 }
1667                 Some(false) => {
1668                     cvt(unsafe { unlinkat(fd, child.name_cstr().as_ptr(), 0) })?;
1669                 }
1670                 None => match cvt(unsafe { unlinkat(fd, child.name_cstr().as_ptr(), 0) }) {
1671                     // type unknown - try to unlink
1672                     Err(err)
1673                         if err.raw_os_error() == Some(libc::EISDIR)
1674                             || err.raw_os_error() == Some(libc::EPERM) =>
1675                     {
1676                         // if the file is a directory unlink fails with EISDIR on Linux and EPERM everyhwere else
1677                         remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1678                     }
1679                     result => {
1680                         result?;
1681                     }
1682                 },
1683             }
1684         }
1685 
1686         // unlink the directory after removing its contents
1687         cvt(unsafe {
1688             unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), pcstr.as_ptr(), libc::AT_REMOVEDIR)
1689         })?;
1690         Ok(())
1691     }
1692 
remove_dir_all(p: &Path) -> io::Result<()>1693     pub fn remove_dir_all(p: &Path) -> io::Result<()> {
1694         // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
1695         // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
1696         // into symlinks.
1697         let attr = lstat(p)?;
1698         if attr.file_type().is_symlink() {
1699             crate::fs::remove_file(p)
1700         } else {
1701             remove_dir_all_recursive(None, p)
1702         }
1703     }
1704 }
1705