1 // miri has some special hacks here that make things unused.
2 #![cfg_attr(miri, allow(unused))]
4 use crate::os
::unix
::prelude
::*;
6 use crate::ffi
::{CStr, OsStr, OsString}
;
8 use crate::io
::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}
;
10 use crate::os
::unix
::io
::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}
;
11 use crate::path
::{Path, PathBuf}
;
14 use crate::sys
::common
::small_c_string
::run_path_with_cstr
;
15 use crate::sys
::fd
::FileDesc
;
16 use crate::sys
::time
::SystemTime
;
17 use crate::sys
::{cvt, cvt_r}
;
18 use crate::sys_common
::{AsInner, AsInnerMut, FromInner, IntoInner}
;
21 all(target_os
= "linux", target_env
= "gnu"),
25 target_os
= "watchos",
27 use crate::sys
::weak
::syscall
;
28 #[cfg(any(target_os = "android", target_os = "macos", target_os = "solaris"))]
29 use crate::sys
::weak
::weak
;
31 use libc
::{c_int, mode_t}
;
37 target_os
= "watchos",
38 target_os
= "solaris",
39 all(target_os
= "linux", target_env
= "gnu")
44 target_os
= "emscripten",
45 target_os
= "android",
49 #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "hurd"))]
52 target_os
= "android",
53 target_os
= "solaris",
54 target_os
= "fuchsia",
56 target_os
= "illumos",
60 use libc
::readdir
as readdir64
;
61 #[cfg(any(target_os = "linux", target_os = "hurd"))]
63 #[cfg(any(target_os = "emscripten", target_os = "l4re"))]
64 use libc
::readdir64_r
;
66 target_os
= "android",
68 target_os
= "emscripten",
69 target_os
= "solaris",
70 target_os
= "illumos",
72 target_os
= "fuchsia",
78 use libc
::readdir_r
as readdir64_r
;
79 #[cfg(target_os = "android")]
81 dirent
as dirent64
, fstat
as fstat64
, fstatat
as fstatat64
, ftruncate64
, lseek64
,
82 lstat
as lstat64
, off64_t
, open
as open64
, stat
as stat64
,
86 target_os
= "emscripten",
88 target_os
= "android",
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
,
97 target_os
= "emscripten",
101 use libc
::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64}
;
103 pub use crate::sys_common
::fs
::try_exists
;
105 pub struct File(FileDesc
);
107 // FIXME: This should be available on Linux with all `target_env`.
108 // But currently only glibc exposes `statx` fn and structs.
109 // We don't want to import unverified raw C structs here directly.
110 // https://github.com/rust-lang/rust/pull/67774
111 macro_rules
! cfg_has_statx
{
112 ({ $($then_tt:tt)* }
else { $($else_tt:tt)* }
) => {
114 if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
121 ($
($block_inner
:tt
)*) => {
122 #[cfg(all(target_os = "linux", target_env = "gnu"))]
131 pub struct FileAttr
{
133 statx_extra_fields
: Option
<StatxExtraFields
>,
137 struct StatxExtraFields
{
138 // This is needed to check if btime is supported by the filesystem.
140 stx_btime
: libc
::statx_timestamp
,
141 // With statx, we can overcome 32-bit `time_t` too.
142 #[cfg(target_pointer_width = "32")]
143 stx_atime
: libc
::statx_timestamp
,
144 #[cfg(target_pointer_width = "32")]
145 stx_ctime
: libc
::statx_timestamp
,
146 #[cfg(target_pointer_width = "32")]
147 stx_mtime
: libc
::statx_timestamp
,
151 // We prefer `statx` on Linux if available, which contains file creation time,
152 // as well as 64-bit timestamps of all kinds.
153 // Default `stat64` contains no creation time and may have 32-bit `time_t`.
159 ) -> Option
<io
::Result
<FileAttr
>> {
160 use crate::sync
::atomic
::{AtomicU8, Ordering}
;
162 // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`.
163 // We check for it on first failure and remember availability to avoid having to
166 enum STATX_STATE{ Unknown = 0, Present, Unavailable }
167 static STATX_SAVED_STATE
: AtomicU8
= AtomicU8
::new(STATX_STATE
::Unknown
as u8);
172 pathname
: *const c_char
,
175 statxbuf
: *mut libc
::statx
179 if STATX_SAVED_STATE
.load(Ordering
::Relaxed
) == STATX_STATE
::Unavailable
as u8 {
183 let mut buf
: libc
::statx
= mem
::zeroed();
184 if let Err(err
) = cvt(statx(fd
, path
, flags
, mask
, &mut buf
)) {
185 if STATX_SAVED_STATE
.load(Ordering
::Relaxed
) == STATX_STATE
::Present
as u8 {
186 return Some(Err(err
));
189 // Availability not checked yet.
191 // First try the cheap way.
192 if err
.raw_os_error() == Some(libc
::ENOSYS
) {
193 STATX_SAVED_STATE
.store(STATX_STATE
::Unavailable
as u8, Ordering
::Relaxed
);
197 // Error other than `ENOSYS` is not a good enough indicator -- it is
198 // known that `EPERM` can be returned as a result of using seccomp to
199 // block the syscall.
200 // Availability is checked by performing a call which expects `EFAULT`
201 // if the syscall is usable.
202 // See: https://github.com/rust-lang/rust/issues/65662
203 // FIXME this can probably just do the call if `EPERM` was received, but
204 // previous iteration of the code checked it for all errors and for now
206 // FIXME what about transient conditions like `ENOMEM`?
207 let err2
= cvt(statx(0, ptr
::null(), 0, libc
::STATX_ALL
, ptr
::null_mut()))
209 .and_then(|e
| e
.raw_os_error());
210 if err2
== Some(libc
::EFAULT
) {
211 STATX_SAVED_STATE
.store(STATX_STATE
::Present
as u8, Ordering
::Relaxed
);
212 return Some(Err(err
));
214 STATX_SAVED_STATE
.store(STATX_STATE
::Unavailable
as u8, Ordering
::Relaxed
);
219 // We cannot fill `stat64` exhaustively because of private padding fields.
220 let mut stat
: stat64
= mem
::zeroed();
221 // `c_ulong` on gnu-mips, `dev_t` otherwise
222 stat
.st_dev
= libc
::makedev(buf
.stx_dev_major
, buf
.stx_dev_minor
) as _
;
223 stat
.st_ino
= buf
.stx_ino
as libc
::ino64_t
;
224 stat
.st_nlink
= buf
.stx_nlink
as libc
::nlink_t
;
225 stat
.st_mode
= buf
.stx_mode
as libc
::mode_t
;
226 stat
.st_uid
= buf
.stx_uid
as libc
::uid_t
;
227 stat
.st_gid
= buf
.stx_gid
as libc
::gid_t
;
228 stat
.st_rdev
= libc
::makedev(buf
.stx_rdev_major
, buf
.stx_rdev_minor
) as _
;
229 stat
.st_size
= buf
.stx_size
as off64_t
;
230 stat
.st_blksize
= buf
.stx_blksize
as libc
::blksize_t
;
231 stat
.st_blocks
= buf
.stx_blocks
as libc
::blkcnt64_t
;
232 stat
.st_atime
= buf
.stx_atime
.tv_sec
as libc
::time_t
;
233 // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
234 stat
.st_atime_nsec
= buf
.stx_atime
.tv_nsec
as _
;
235 stat
.st_mtime
= buf
.stx_mtime
.tv_sec
as libc
::time_t
;
236 stat
.st_mtime_nsec
= buf
.stx_mtime
.tv_nsec
as _
;
237 stat
.st_ctime
= buf
.stx_ctime
.tv_sec
as libc
::time_t
;
238 stat
.st_ctime_nsec
= buf
.stx_ctime
.tv_nsec
as _
;
240 let extra
= StatxExtraFields
{
241 stx_mask
: buf
.stx_mask
,
242 stx_btime
: buf
.stx_btime
,
243 // Store full times to avoid 32-bit `time_t` truncation.
244 #[cfg(target_pointer_width = "32")]
245 stx_atime
: buf
.stx_atime
,
246 #[cfg(target_pointer_width = "32")]
247 stx_ctime
: buf
.stx_ctime
,
248 #[cfg(target_pointer_width = "32")]
249 stx_mtime
: buf
.stx_mtime
,
252 Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }
))
257 pub struct FileAttr
{
262 // all DirEntry's will have a reference to this struct
263 struct InnerReadDir
{
269 inner
: Arc
<InnerReadDir
>,
274 fn new(inner
: InnerReadDir
) -> Self {
275 Self { inner: Arc::new(inner), end_of_stream: false }
279 struct Dir(*mut libc
::DIR
);
281 unsafe impl Send
for Dir {}
282 unsafe impl Sync
for Dir {}
285 target_os
= "android",
287 target_os
= "solaris",
288 target_os
= "illumos",
289 target_os
= "fuchsia",
295 pub struct DirEntry
{
296 dir
: Arc
<InnerReadDir
>,
298 // We need to store an owned copy of the entry name on platforms that use
299 // readdir() (not readdir_r()), because a) struct dirent may use a flexible
300 // array to store the name, b) it lives only until the next readdir() call.
301 name
: crate::ffi
::CString
,
304 // Define a minimal subset of fields we need from `dirent64`, especially since
305 // we're not using the immediate `d_name` on these targets. Keeping this as an
306 // `entry` field in `DirEntry` helps reduce the `cfg` boilerplate elsewhere.
308 target_os
= "android",
310 target_os
= "solaris",
311 target_os
= "illumos",
312 target_os
= "fuchsia",
318 struct dirent64_min
{
321 target_os
= "solaris",
322 target_os
= "illumos",
330 target_os
= "android",
332 target_os
= "solaris",
333 target_os
= "illumos",
334 target_os
= "fuchsia",
340 pub struct DirEntry
{
341 dir
: Arc
<InnerReadDir
>,
342 // The full entry includes a fixed-length `d_name`.
346 #[derive(Clone, Debug)]
347 pub struct OpenOptions
{
360 #[derive(Clone, PartialEq, Eq, Debug)]
361 pub struct FilePermissions
{
365 #[derive(Copy, Clone, Debug, Default)]
366 pub struct FileTimes
{
367 accessed
: Option
<SystemTime
>,
368 modified
: Option
<SystemTime
>,
369 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
370 created
: Option
<SystemTime
>,
373 #[derive(Copy, Clone, Eq, Debug)]
374 pub struct FileType
{
378 impl PartialEq
for FileType
{
379 fn eq(&self, other
: &Self) -> bool
{
380 self.masked() == other
.masked()
384 impl core
::hash
::Hash
for FileType
{
385 fn hash
<H
: core
::hash
::Hasher
>(&self, state
: &mut H
) {
386 self.masked().hash(state
);
391 pub struct DirBuilder
{
397 fn from_stat64(stat
: stat64
) -> Self {
398 Self { stat, statx_extra_fields: None }
401 #[cfg(target_pointer_width = "32")]
402 pub fn stx_mtime(&self) -> Option
<&libc
::statx_timestamp
> {
403 if let Some(ext
) = &self.statx_extra_fields
{
404 if (ext
.stx_mask
& libc
::STATX_MTIME
) != 0 {
405 return Some(&ext
.stx_mtime
);
411 #[cfg(target_pointer_width = "32")]
412 pub fn stx_atime(&self) -> Option
<&libc
::statx_timestamp
> {
413 if let Some(ext
) = &self.statx_extra_fields
{
414 if (ext
.stx_mask
& libc
::STATX_ATIME
) != 0 {
415 return Some(&ext
.stx_atime
);
421 #[cfg(target_pointer_width = "32")]
422 pub fn stx_ctime(&self) -> Option
<&libc
::statx_timestamp
> {
423 if let Some(ext
) = &self.statx_extra_fields
{
424 if (ext
.stx_mask
& libc
::STATX_CTIME
) != 0 {
425 return Some(&ext
.stx_ctime
);
433 fn from_stat64(stat
: stat64
) -> Self {
440 pub fn size(&self) -> u64 {
441 self.stat
.st_size
as u64
443 pub fn perm(&self) -> FilePermissions
{
444 FilePermissions { mode: (self.stat.st_mode as mode_t) }
447 pub fn file_type(&self) -> FileType
{
448 FileType { mode: self.stat.st_mode as mode_t }
452 #[cfg(target_os = "netbsd")]
454 pub fn modified(&self) -> io
::Result
<SystemTime
> {
455 Ok(SystemTime
::new(self.stat
.st_mtime
as i64, self.stat
.st_mtimensec
as i64))
458 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
459 Ok(SystemTime
::new(self.stat
.st_atime
as i64, self.stat
.st_atimensec
as i64))
462 pub fn created(&self) -> io
::Result
<SystemTime
> {
463 Ok(SystemTime
::new(self.stat
.st_birthtime
as i64, self.stat
.st_birthtimensec
as i64))
467 #[cfg(not(any(target_os = "netbsd", target_os = "nto")))]
470 target_os
= "vxworks",
471 target_os
= "espidf",
472 target_os
= "horizon",
476 pub fn modified(&self) -> io
::Result
<SystemTime
> {
477 #[cfg(target_pointer_width = "32")]
479 if let Some(mtime
) = self.stx_mtime() {
480 return Ok(SystemTime
::new(mtime
.tv_sec
, mtime
.tv_nsec
as i64));
484 Ok(SystemTime
::new(self.stat
.st_mtime
as i64, self.stat
.st_mtime_nsec
as i64))
487 #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))]
488 pub fn modified(&self) -> io
::Result
<SystemTime
> {
489 Ok(SystemTime
::new(self.stat
.st_mtime
as i64, 0))
492 #[cfg(any(target_os = "horizon", target_os = "hurd"))]
493 pub fn modified(&self) -> io
::Result
<SystemTime
> {
494 Ok(SystemTime
::from(self.stat
.st_mtim
))
498 target_os
= "vxworks",
499 target_os
= "espidf",
500 target_os
= "horizon",
504 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
505 #[cfg(target_pointer_width = "32")]
507 if let Some(atime
) = self.stx_atime() {
508 return Ok(SystemTime
::new(atime
.tv_sec
, atime
.tv_nsec
as i64));
512 Ok(SystemTime
::new(self.stat
.st_atime
as i64, self.stat
.st_atime_nsec
as i64))
515 #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))]
516 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
517 Ok(SystemTime
::new(self.stat
.st_atime
as i64, 0))
520 #[cfg(any(target_os = "horizon", target_os = "hurd"))]
521 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
522 Ok(SystemTime
::from(self.stat
.st_atim
))
526 target_os
= "freebsd",
527 target_os
= "openbsd",
531 target_os
= "watchos",
533 pub fn created(&self) -> io
::Result
<SystemTime
> {
534 Ok(SystemTime
::new(self.stat
.st_birthtime
as i64, self.stat
.st_birthtime_nsec
as i64))
538 target_os
= "freebsd",
539 target_os
= "openbsd",
543 target_os
= "watchos",
546 pub fn created(&self) -> io
::Result
<SystemTime
> {
548 if let Some(ext
) = &self.statx_extra_fields
{
549 return if (ext
.stx_mask
& libc
::STATX_BTIME
) != 0 {
550 Ok(SystemTime
::new(ext
.stx_btime
.tv_sec
, ext
.stx_btime
.tv_nsec
as i64))
552 Err(io
::const_io_error
!(
553 io
::ErrorKind
::Uncategorized
,
554 "creation time is not available for the filesystem",
560 Err(io
::const_io_error
!(
561 io
::ErrorKind
::Unsupported
,
562 "creation time is not available on this platform \
567 #[cfg(target_os = "vita")]
568 pub fn created(&self) -> io
::Result
<SystemTime
> {
569 Ok(SystemTime
::new(self.stat
.st_ctime
as i64, 0))
573 #[cfg(target_os = "nto")]
575 pub fn modified(&self) -> io
::Result
<SystemTime
> {
576 Ok(SystemTime
::new(self.stat
.st_mtim
.tv_sec
, self.stat
.st_mtim
.tv_nsec
))
579 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
580 Ok(SystemTime
::new(self.stat
.st_atim
.tv_sec
, self.stat
.st_atim
.tv_nsec
))
583 pub fn created(&self) -> io
::Result
<SystemTime
> {
584 Ok(SystemTime
::new(self.stat
.st_ctim
.tv_sec
, self.stat
.st_ctim
.tv_nsec
))
588 impl AsInner
<stat64
> for FileAttr
{
590 fn as_inner(&self) -> &stat64
{
595 impl FilePermissions
{
596 pub fn readonly(&self) -> bool
{
597 // check if any class (owner, group, others) has write permission
598 self.mode
& 0o222 == 0
601 pub fn set_readonly(&mut self, readonly
: bool
) {
603 // remove write permission for all classes; equivalent to `chmod a-w <file>`
606 // add write permission for all classes; equivalent to `chmod a+w <file>`
610 pub fn mode(&self) -> u32 {
616 pub fn set_accessed(&mut self, t
: SystemTime
) {
617 self.accessed
= Some(t
);
620 pub fn set_modified(&mut self, t
: SystemTime
) {
621 self.modified
= Some(t
);
624 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
625 pub fn set_created(&mut self, t
: SystemTime
) {
626 self.created
= Some(t
);
631 pub fn is_dir(&self) -> bool
{
632 self.is(libc
::S_IFDIR
)
634 pub fn is_file(&self) -> bool
{
635 self.is(libc
::S_IFREG
)
637 pub fn is_symlink(&self) -> bool
{
638 self.is(libc
::S_IFLNK
)
641 pub fn is(&self, mode
: mode_t
) -> bool
{
642 self.masked() == mode
645 fn masked(&self) -> mode_t
{
646 self.mode
& libc
::S_IFMT
650 impl FromInner
<u32> for FilePermissions
{
651 fn from_inner(mode
: u32) -> FilePermissions
{
652 FilePermissions { mode: mode as mode_t }
656 impl fmt
::Debug
for ReadDir
{
657 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
658 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
659 // Thus the result will be e g 'ReadDir("/home")'
660 fmt
::Debug
::fmt(&*self.inner
.root
, f
)
664 impl Iterator
for ReadDir
{
665 type Item
= io
::Result
<DirEntry
>;
668 target_os
= "android",
670 target_os
= "solaris",
671 target_os
= "fuchsia",
673 target_os
= "illumos",
678 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
679 if self.end_of_stream
{
685 // As of POSIX.1-2017, readdir() is not required to be thread safe; only
686 // readdir_r() is. However, readdir_r() cannot correctly handle platforms
687 // with unlimited or variable NAME_MAX. Many modern platforms guarantee
688 // thread safety for readdir() as long an individual DIR* is not accessed
689 // concurrently, which is sufficient for Rust.
690 super::os
::set_errno(0);
691 let entry_ptr
= readdir64(self.inner
.dirp
.0);
692 if entry_ptr
.is_null() {
693 // We either encountered an error, or reached the end. Either way,
694 // the next call to next() should return None.
695 self.end_of_stream
= true;
697 // To distinguish between errors and end-of-directory, we had to clear
698 // errno beforehand to check for an error now.
699 return match super::os
::errno() {
701 e
=> Some(Err(Error
::from_raw_os_error(e
))),
705 // The dirent64 struct is a weird imaginary thing that isn't ever supposed
706 // to be worked with by value. Its trailing d_name field is declared
707 // variously as [c_char; 256] or [c_char; 1] on different systems but
708 // either way that size is meaningless; only the offset of d_name is
709 // meaningful. The dirent64 pointers that libc returns from readdir64 are
710 // allowed to point to allocations smaller _or_ LARGER than implied by the
711 // definition of the struct.
713 // As such, we need to be even more careful with dirent64 than if its
714 // contents were "simply" partially initialized data.
716 // Like for uninitialized contents, converting entry_ptr to `&dirent64`
717 // would not be legal. However, unique to dirent64 is that we don't even
718 // get to use `addr_of!((*entry_ptr).d_name)` because that operation
719 // requires the full extent of *entry_ptr to be in bounds of the same
720 // allocation, which is not necessarily the case here.
722 // Instead we must access fields individually through their offsets.
723 macro_rules
! offset_ptr
{
724 ($entry_ptr
:expr
, $field
:ident
) => {{
725 const OFFSET
: isize = mem
::offset_of
!(dirent64
, $field
) as isize;
727 // Cast to the same type determined by the else branch.
728 $entry_ptr
.byte_offset(OFFSET
).cast
::<_
>()
730 #[allow(deref_nullptr)]
732 ptr
::addr_of
!((*ptr
::null
::<dirent64
>()).$field
)
738 // d_name is guaranteed to be null-terminated.
739 let name
= CStr
::from_ptr(offset_ptr
!(entry_ptr
, d_name
).cast());
740 let name_bytes
= name
.to_bytes();
741 if name_bytes
== b
"." || name_bytes
== b
".." {
745 #[cfg(not(target_os = "vita"))]
746 let entry
= dirent64_min
{
747 d_ino
: *offset_ptr
!(entry_ptr
, d_ino
) as u64,
749 target_os
= "solaris",
750 target_os
= "illumos",
753 d_type
: *offset_ptr
!(entry_ptr
, d_type
) as u8,
756 #[cfg(target_os = "vita")]
757 let entry
= dirent64_min { d_ino: 0u64 }
;
759 return Some(Ok(DirEntry
{
761 name
: name
.to_owned(),
762 dir
: Arc
::clone(&self.inner
),
769 target_os
= "android",
771 target_os
= "solaris",
772 target_os
= "fuchsia",
774 target_os
= "illumos",
779 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
780 if self.end_of_stream
{
785 let mut ret
= DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) }
;
786 let mut entry_ptr
= ptr
::null_mut();
788 let err
= readdir64_r(self.inner
.dirp
.0, &mut ret
.entry
, &mut entry_ptr
);
790 if entry_ptr
.is_null() {
791 // We encountered an error (which will be returned in this iteration), but
792 // we also reached the end of the directory stream. The `end_of_stream`
793 // flag is enabled to make sure that we return `None` in the next iteration
794 // (instead of looping forever)
795 self.end_of_stream
= true;
797 return Some(Err(Error
::from_raw_os_error(err
)));
799 if entry_ptr
.is_null() {
802 if ret
.name_bytes() != b
"." && ret
.name_bytes() != b
".." {
803 return Some(Ok(ret
));
812 let r
= unsafe { libc::closedir(self.0) }
;
814 r
== 0 || crate::io
::Error
::last_os_error().is_interrupted(),
815 "unexpected error during closedir: {:?}",
816 crate::io
::Error
::last_os_error()
822 pub fn path(&self) -> PathBuf
{
823 self.dir
.root
.join(self.file_name_os_str())
826 pub fn file_name(&self) -> OsString
{
827 self.file_name_os_str().to_os_string()
833 target_os
= "emscripten",
834 target_os
= "android",
839 pub fn metadata(&self) -> io
::Result
<FileAttr
> {
840 let fd
= cvt(unsafe { dirfd(self.dir.dirp.0) }
)?
;
841 let name
= self.name_cstr().as_ptr();
844 if let Some(ret
) = unsafe { try_statx(
847 libc
::AT_SYMLINK_NOFOLLOW
| libc
::AT_STATX_SYNC_AS_STAT
,
854 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
855 cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) }
)?
;
856 Ok(FileAttr
::from_stat64(stat
))
862 target_os
= "emscripten",
863 target_os
= "android",
868 pub fn metadata(&self) -> io
::Result
<FileAttr
> {
873 target_os
= "solaris",
874 target_os
= "illumos",
876 target_os
= "vxworks",
880 pub fn file_type(&self) -> io
::Result
<FileType
> {
881 self.metadata().map(|m
| m
.file_type())
885 target_os
= "solaris",
886 target_os
= "illumos",
888 target_os
= "vxworks",
892 pub fn file_type(&self) -> io
::Result
<FileType
> {
893 match self.entry
.d_type
{
894 libc
::DT_CHR
=> Ok(FileType { mode: libc::S_IFCHR }
),
895 libc
::DT_FIFO
=> Ok(FileType { mode: libc::S_IFIFO }
),
896 libc
::DT_LNK
=> Ok(FileType { mode: libc::S_IFLNK }
),
897 libc
::DT_REG
=> Ok(FileType { mode: libc::S_IFREG }
),
898 libc
::DT_SOCK
=> Ok(FileType { mode: libc::S_IFSOCK }
),
899 libc
::DT_DIR
=> Ok(FileType { mode: libc::S_IFDIR }
),
900 libc
::DT_BLK
=> Ok(FileType { mode: libc::S_IFBLK }
),
901 _
=> self.metadata().map(|m
| m
.file_type()),
909 target_os
= "watchos",
911 target_os
= "emscripten",
912 target_os
= "android",
913 target_os
= "solaris",
914 target_os
= "illumos",
917 target_os
= "fuchsia",
919 target_os
= "vxworks",
920 target_os
= "espidf",
921 target_os
= "horizon",
926 pub fn ino(&self) -> u64 {
927 self.entry
.d_ino
as u64
931 target_os
= "freebsd",
932 target_os
= "openbsd",
933 target_os
= "netbsd",
934 target_os
= "dragonfly"
936 pub fn ino(&self) -> u64 {
937 self.entry
.d_fileno
as u64
944 target_os
= "watchos",
945 target_os
= "netbsd",
946 target_os
= "openbsd",
947 target_os
= "freebsd",
948 target_os
= "dragonfly"
950 fn name_bytes(&self) -> &[u8] {
953 slice
::from_raw_parts(
954 self.entry
.d_name
.as_ptr() as *const u8,
955 self.entry
.d_namlen
as usize,
963 target_os
= "watchos",
964 target_os
= "netbsd",
965 target_os
= "openbsd",
966 target_os
= "freebsd",
967 target_os
= "dragonfly"
969 fn name_bytes(&self) -> &[u8] {
970 self.name_cstr().to_bytes()
974 target_os
= "android",
976 target_os
= "solaris",
977 target_os
= "illumos",
978 target_os
= "fuchsia",
984 fn name_cstr(&self) -> &CStr
{
985 unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
988 target_os
= "android",
990 target_os
= "solaris",
991 target_os
= "illumos",
992 target_os
= "fuchsia",
998 fn name_cstr(&self) -> &CStr
{
1002 pub fn file_name_os_str(&self) -> &OsStr
{
1003 OsStr
::from_bytes(self.name_bytes())
1008 pub fn new() -> OpenOptions
{
1023 pub fn read(&mut self, read
: bool
) {
1026 pub fn write(&mut self, write
: bool
) {
1029 pub fn append(&mut self, append
: bool
) {
1030 self.append
= append
;
1032 pub fn truncate(&mut self, truncate
: bool
) {
1033 self.truncate
= truncate
;
1035 pub fn create(&mut self, create
: bool
) {
1036 self.create
= create
;
1038 pub fn create_new(&mut self, create_new
: bool
) {
1039 self.create_new
= create_new
;
1042 pub fn custom_flags(&mut self, flags
: i32) {
1043 self.custom_flags
= flags
;
1045 pub fn mode(&mut self, mode
: u32) {
1046 self.mode
= mode
as mode_t
;
1049 fn get_access_mode(&self) -> io
::Result
<c_int
> {
1050 match (self.read
, self.write
, self.append
) {
1051 (true, false, false) => Ok(libc
::O_RDONLY
),
1052 (false, true, false) => Ok(libc
::O_WRONLY
),
1053 (true, true, false) => Ok(libc
::O_RDWR
),
1054 (false, _
, true) => Ok(libc
::O_WRONLY
| libc
::O_APPEND
),
1055 (true, _
, true) => Ok(libc
::O_RDWR
| libc
::O_APPEND
),
1056 (false, false, false) => Err(Error
::from_raw_os_error(libc
::EINVAL
)),
1060 fn get_creation_mode(&self) -> io
::Result
<c_int
> {
1061 match (self.write
, self.append
) {
1064 if self.truncate
|| self.create
|| self.create_new
{
1065 return Err(Error
::from_raw_os_error(libc
::EINVAL
));
1069 if self.truncate
&& !self.create_new
{
1070 return Err(Error
::from_raw_os_error(libc
::EINVAL
));
1075 Ok(match (self.create
, self.truncate
, self.create_new
) {
1076 (false, false, false) => 0,
1077 (true, false, false) => libc
::O_CREAT
,
1078 (false, true, false) => libc
::O_TRUNC
,
1079 (true, true, false) => libc
::O_CREAT
| libc
::O_TRUNC
,
1080 (_
, _
, true) => libc
::O_CREAT
| libc
::O_EXCL
,
1086 pub fn open(path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
1087 run_path_with_cstr(path
, |path
| File
::open_c(path
, opts
))
1090 pub fn open_c(path
: &CStr
, opts
: &OpenOptions
) -> io
::Result
<File
> {
1091 let flags
= libc
::O_CLOEXEC
1092 | opts
.get_access_mode()?
1093 | opts
.get_creation_mode()?
1094 | (opts
.custom_flags
as c_int
& !libc
::O_ACCMODE
);
1095 // The third argument of `open64` is documented to have type `mode_t`. On
1096 // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
1097 // However, since this is a variadic function, C integer promotion rules mean that on
1098 // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
1099 let fd
= cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) }
)?
;
1100 Ok(File(unsafe { FileDesc::from_raw_fd(fd) }
))
1103 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
1104 let fd
= self.as_raw_fd();
1107 if let Some(ret
) = unsafe { try_statx(
1109 b
"\0" as *const _
as *const c_char
,
1110 libc
::AT_EMPTY_PATH
| libc
::AT_STATX_SYNC_AS_STAT
,
1117 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
1118 cvt(unsafe { fstat64(fd, &mut stat) }
)?
;
1119 Ok(FileAttr
::from_stat64(stat
))
1122 pub fn fsync(&self) -> io
::Result
<()> {
1123 cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) }
)?
;
1127 target_os
= "macos",
1130 target_os
= "watchos",
1132 unsafe fn os_fsync(fd
: c_int
) -> c_int
{
1133 libc
::fcntl(fd
, libc
::F_FULLFSYNC
)
1136 target_os
= "macos",
1139 target_os
= "watchos",
1141 unsafe fn os_fsync(fd
: c_int
) -> c_int
{
1146 pub fn datasync(&self) -> io
::Result
<()> {
1147 cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) }
)?
;
1151 target_os
= "macos",
1154 target_os
= "watchos",
1156 unsafe fn os_datasync(fd
: c_int
) -> c_int
{
1157 libc
::fcntl(fd
, libc
::F_FULLFSYNC
)
1160 target_os
= "freebsd",
1161 target_os
= "linux",
1162 target_os
= "android",
1163 target_os
= "netbsd",
1164 target_os
= "openbsd",
1168 unsafe fn os_datasync(fd
: c_int
) -> c_int
{
1172 target_os
= "android",
1173 target_os
= "freebsd",
1176 target_os
= "linux",
1177 target_os
= "macos",
1178 target_os
= "netbsd",
1179 target_os
= "openbsd",
1180 target_os
= "watchos",
1184 unsafe fn os_datasync(fd
: c_int
) -> c_int
{
1189 pub fn truncate(&self, size
: u64) -> io
::Result
<()> {
1191 size
.try_into().map_err(|e
| io
::Error
::new(io
::ErrorKind
::InvalidInput
, e
))?
;
1192 cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }
).map(drop
)
1195 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
1199 pub fn read_vectored(&self, bufs
: &mut [IoSliceMut
<'_
>]) -> io
::Result
<usize> {
1200 self.0.read_vectored(bufs
)
1204 pub fn is_read_vectored(&self) -> bool
{
1205 self.0.is_read_vectored
()
1208 pub fn read_at(&self, buf
: &mut [u8], offset
: u64) -> io
::Result
<usize> {
1209 self.0.read_at(buf
, offset
)
1212 pub fn read_buf(&self, cursor
: BorrowedCursor
<'_
>) -> io
::Result
<()> {
1213 self.0.read_buf(cursor
)
1216 pub fn read_vectored_at(&self, bufs
: &mut [IoSliceMut
<'_
>], offset
: u64) -> io
::Result
<usize> {
1217 self.0.read_vectored_at(bufs
, offset
)
1220 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
1224 pub fn write_vectored(&self, bufs
: &[IoSlice
<'_
>]) -> io
::Result
<usize> {
1225 self.0.write_vectored(bufs
)
1229 pub fn is_write_vectored(&self) -> bool
{
1230 self.0.is_write_vectored
()
1233 pub fn write_at(&self, buf
: &[u8], offset
: u64) -> io
::Result
<usize> {
1234 self.0.write_at(buf
, offset
)
1237 pub fn write_vectored_at(&self, bufs
: &[IoSlice
<'_
>], offset
: u64) -> io
::Result
<usize> {
1238 self.0.write_vectored_at(bufs
, offset
)
1242 pub fn flush(&self) -> io
::Result
<()> {
1246 pub fn seek(&self, pos
: SeekFrom
) -> io
::Result
<u64> {
1247 let (whence
, pos
) = match pos
{
1248 // Casting to `i64` is fine, too large values will end up as
1249 // negative which will cause an error in `lseek64`.
1250 SeekFrom
::Start(off
) => (libc
::SEEK_SET
, off
as i64),
1251 SeekFrom
::End(off
) => (libc
::SEEK_END
, off
),
1252 SeekFrom
::Current(off
) => (libc
::SEEK_CUR
, off
),
1254 let n
= cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) }
)?
;
1258 pub fn duplicate(&self) -> io
::Result
<File
> {
1259 self.0.duplicate().map(File
)
1262 pub fn set_permissions(&self, perm
: FilePermissions
) -> io
::Result
<()> {
1263 cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) }
)?
;
1267 pub fn set_times(&self, times
: FileTimes
) -> io
::Result
<()> {
1268 #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))]
1269 let to_timespec
= |time
: Option
<SystemTime
>| {
1271 Some(time
) if let Some(ts
) = time
.t
.to_timespec() => Ok(ts
),
1272 Some(time
) if time
> crate::sys
::time
::UNIX_EPOCH
=> Err(io
::const_io_error
!(io
::ErrorKind
::InvalidInput
, "timestamp is too large to set as a file time")),
1273 Some(_
) => Err(io
::const_io_error
!(io
::ErrorKind
::InvalidInput
, "timestamp is too small to set as a file time")),
1274 None
=> Ok(libc
::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }
),
1278 if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] {
1279 // Redox doesn't appear to support `UTIME_OMIT`.
1280 // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore
1281 // the same as for Redox.
1283 Err(io
::const_io_error
!(
1284 io
::ErrorKind
::Unsupported
,
1285 "setting file times not supported",
1287 } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))] {
1288 let mut buf
= [mem
::MaybeUninit
::<libc
::timespec
>::uninit(); 3];
1289 let mut num_times
= 0;
1290 let mut attrlist
: libc
::attrlist
= unsafe { mem::zeroed() }
;
1291 attrlist
.bitmapcount
= libc
::ATTR_BIT_MAP_COUNT
;
1292 if times
.created
.is_some() {
1293 buf
[num_times
].write(to_timespec(times
.created
)?
);
1295 attrlist
.commonattr
|= libc
::ATTR_CMN_CRTIME
;
1297 if times
.modified
.is_some() {
1298 buf
[num_times
].write(to_timespec(times
.modified
)?
);
1300 attrlist
.commonattr
|= libc
::ATTR_CMN_MODTIME
;
1302 if times
.accessed
.is_some() {
1303 buf
[num_times
].write(to_timespec(times
.accessed
)?
);
1305 attrlist
.commonattr
|= libc
::ATTR_CMN_ACCTIME
;
1307 cvt(unsafe { libc
::fsetattrlist(
1309 (&attrlist
as *const libc
::attrlist
).cast
::<libc
::c_void
>().cast_mut(),
1310 buf
.as_ptr().cast
::<libc
::c_void
>().cast_mut(),
1311 num_times
* mem
::size_of
::<libc
::timespec
>(),
1315 } else if #[cfg(target_os = "android")] {
1316 let times
= [to_timespec(times
.accessed
)?
, to_timespec(times
.modified
)?
];
1317 // futimens requires Android API level 19
1319 weak
!(fn futimens(c_int
, *const libc
::timespec
) -> c_int
);
1320 match futimens
.get() {
1321 Some(futimens
) => futimens(self.as_raw_fd(), times
.as_ptr()),
1322 None
=> return Err(io
::const_io_error
!(
1323 io
::ErrorKind
::Unsupported
,
1324 "setting file times requires Android API level >= 19",
1330 #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
1332 use crate::sys
::{time::__timespec64, weak::weak}
;
1334 // Added in glibc 2.34
1335 weak
!(fn __futimens64(libc
::c_int
, *const __timespec64
) -> libc
::c_int
);
1337 if let Some(futimens64
) = __futimens64
.get() {
1338 let to_timespec
= |time
: Option
<SystemTime
>| time
.map(|time
| time
.t
.to_timespec64())
1339 .unwrap_or(__timespec64
::new(0, libc
::UTIME_OMIT
as _
));
1340 let times
= [to_timespec(times
.accessed
), to_timespec(times
.modified
)];
1341 cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) }
)?
;
1345 let times
= [to_timespec(times
.accessed
)?
, to_timespec(times
.modified
)?
];
1346 cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) }
)?
;
1354 pub fn new() -> DirBuilder
{
1355 DirBuilder { mode: 0o777 }
1358 pub fn mkdir(&self, p
: &Path
) -> io
::Result
<()> {
1359 run_path_with_cstr(p
, |p
| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }
).map(|_
| ()))
1362 pub fn set_mode(&mut self, mode
: u32) {
1363 self.mode
= mode
as mode_t
;
1367 impl AsInner
<FileDesc
> for File
{
1369 fn as_inner(&self) -> &FileDesc
{
1374 impl AsInnerMut
<FileDesc
> for File
{
1376 fn as_inner_mut(&mut self) -> &mut FileDesc
{
1381 impl IntoInner
<FileDesc
> for File
{
1382 fn into_inner(self) -> FileDesc
{
1387 impl FromInner
<FileDesc
> for File
{
1388 fn from_inner(file_desc
: FileDesc
) -> Self {
1393 impl AsFd
for File
{
1394 fn as_fd(&self) -> BorrowedFd
<'_
> {
1399 impl AsRawFd
for File
{
1401 fn as_raw_fd(&self) -> RawFd
{
1406 impl IntoRawFd
for File
{
1407 fn into_raw_fd(self) -> RawFd
{
1408 self.0.into_raw_fd
()
1412 impl FromRawFd
for File
{
1413 unsafe fn from_raw_fd(raw_fd
: RawFd
) -> Self {
1414 Self(FromRawFd
::from_raw_fd(raw_fd
))
1418 impl fmt
::Debug
for File
{
1419 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
1421 target_os
= "linux",
1422 target_os
= "netbsd",
1423 target_os
= "illumos",
1424 target_os
= "solaris"
1426 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
1427 let mut p
= PathBuf
::from("/proc/self/fd");
1428 p
.push(&fd
.to_string());
1432 #[cfg(target_os = "macos")]
1433 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
1434 // FIXME: The use of PATH_MAX is generally not encouraged, but it
1435 // is inevitable in this case because macOS defines `fcntl` with
1436 // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
1437 // alternatives. If a better method is invented, it should be used
1439 let mut buf
= vec
![0; libc
::PATH_MAX
as usize];
1440 let n
= unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }
;
1444 let l
= buf
.iter().position(|&c
| c
== 0).unwrap();
1445 buf
.truncate(l
as usize);
1446 buf
.shrink_to_fit();
1447 Some(PathBuf
::from(OsString
::from_vec(buf
)))
1450 #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
1451 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
1452 let info
= Box
::<libc
::kinfo_file
>::new_zeroed();
1453 let mut info
= unsafe { info.assume_init() }
;
1454 info
.kf_structsize
= mem
::size_of
::<libc
::kinfo_file
>() as libc
::c_int
;
1455 let n
= unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) }
;
1459 let buf
= unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() }
;
1460 Some(PathBuf
::from(OsString
::from_vec(buf
)))
1463 #[cfg(target_os = "vxworks")]
1464 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
1465 let mut buf
= vec
![0; libc
::PATH_MAX
as usize];
1466 let n
= unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) }
;
1470 let l
= buf
.iter().position(|&c
| c
== 0).unwrap();
1471 buf
.truncate(l
as usize);
1472 Some(PathBuf
::from(OsString
::from_vec(buf
)))
1476 target_os
= "linux",
1477 target_os
= "macos",
1478 target_os
= "vxworks",
1479 all(target_os
= "freebsd", target_arch
= "x86_64"),
1480 target_os
= "netbsd",
1481 target_os
= "illumos",
1482 target_os
= "solaris"
1484 fn get_path(_fd
: c_int
) -> Option
<PathBuf
> {
1485 // FIXME(#24570): implement this for other Unix platforms
1490 target_os
= "linux",
1491 target_os
= "macos",
1492 target_os
= "freebsd",
1494 target_os
= "netbsd",
1495 target_os
= "openbsd",
1496 target_os
= "vxworks"
1498 fn get_mode(fd
: c_int
) -> Option
<(bool
, bool
)> {
1499 let mode
= unsafe { libc::fcntl(fd, libc::F_GETFL) }
;
1503 match mode
& libc
::O_ACCMODE
{
1504 libc
::O_RDONLY
=> Some((true, false)),
1505 libc
::O_RDWR
=> Some((true, true)),
1506 libc
::O_WRONLY
=> Some((false, true)),
1512 target_os
= "linux",
1513 target_os
= "macos",
1514 target_os
= "freebsd",
1516 target_os
= "netbsd",
1517 target_os
= "openbsd",
1518 target_os
= "vxworks"
1520 fn get_mode(_fd
: c_int
) -> Option
<(bool
, bool
)> {
1521 // FIXME(#24570): implement this for other Unix platforms
1525 let fd
= self.as_raw_fd();
1526 let mut b
= f
.debug_struct("File");
1528 if let Some(path
) = get_path(fd
) {
1529 b
.field("path", &path
);
1531 if let Some((read
, write
)) = get_mode(fd
) {
1532 b
.field("read", &read
).field("write", &write
);
1538 pub fn readdir(path
: &Path
) -> io
::Result
<ReadDir
> {
1539 let ptr
= run_path_with_cstr(path
, |p
| unsafe { Ok(libc::opendir(p.as_ptr())) }
)?
;
1541 Err(Error
::last_os_error())
1543 let root
= path
.to_path_buf();
1544 let inner
= InnerReadDir { dirp: Dir(ptr), root }
;
1545 Ok(ReadDir
::new(inner
))
1549 pub fn unlink(p
: &Path
) -> io
::Result
<()> {
1550 run_path_with_cstr(p
, |p
| cvt(unsafe { libc::unlink(p.as_ptr()) }
).map(|_
| ()))
1553 pub fn rename(old
: &Path
, new
: &Path
) -> io
::Result
<()> {
1554 run_path_with_cstr(old
, |old
| {
1555 run_path_with_cstr(new
, |new
| {
1556 cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }
).map(|_
| ())
1561 pub fn set_perm(p
: &Path
, perm
: FilePermissions
) -> io
::Result
<()> {
1562 run_path_with_cstr(p
, |p
| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }
).map(|_
| ()))
1565 pub fn rmdir(p
: &Path
) -> io
::Result
<()> {
1566 run_path_with_cstr(p
, |p
| cvt(unsafe { libc::rmdir(p.as_ptr()) }
).map(|_
| ()))
1569 pub fn readlink(p
: &Path
) -> io
::Result
<PathBuf
> {
1570 run_path_with_cstr(p
, |c_path
| {
1571 let p
= c_path
.as_ptr();
1573 let mut buf
= Vec
::with_capacity(256);
1577 cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) }
)?
1581 buf
.set_len(buf_read
);
1584 if buf_read
!= buf
.capacity() {
1585 buf
.shrink_to_fit();
1587 return Ok(PathBuf
::from(OsString
::from_vec(buf
)));
1590 // Trigger the internal buffer resizing logic of `Vec` by requiring
1591 // more space than the current capacity. The length is guaranteed to be
1592 // the same as the capacity due to the if statement above.
1598 pub fn symlink(original
: &Path
, link
: &Path
) -> io
::Result
<()> {
1599 run_path_with_cstr(original
, |original
| {
1600 run_path_with_cstr(link
, |link
| {
1601 cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }
).map(|_
| ())
1606 pub fn link(original
: &Path
, link
: &Path
) -> io
::Result
<()> {
1607 run_path_with_cstr(original
, |original
| {
1608 run_path_with_cstr(link
, |link
| {
1610 if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] {
1611 // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
1612 // it implementation-defined whether `link` follows symlinks, so rely on the
1613 // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
1614 // Android has `linkat` on newer versions, but we happen to know `link`
1615 // always has the correct behavior, so it's here as well.
1616 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) }
)?
;
1617 } else if #[cfg(any(target_os = "macos", target_os = "solaris"))] {
1618 // MacOS (<=10.9) and Solaris 10 lack support for linkat while newer
1619 // versions have it. We want to use linkat if it is available, so we use weak!
1620 // to check. `linkat` is preferable to `link` because it gives us a flag to
1621 // specify how symlinks should be handled. We pass 0 as the flags argument,
1622 // meaning it shouldn't follow symlinks.
1623 weak
!(fn linkat(c_int
, *const c_char
, c_int
, *const c_char
, c_int
) -> c_int
);
1625 if let Some(f
) = linkat
.get() {
1626 cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) }
)?
;
1628 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) }
)?
;
1631 // Where we can, use `linkat` instead of `link`; see the comment above
1632 // this one for details on why.
1633 cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) }
)?
;
1641 pub fn stat(p
: &Path
) -> io
::Result
<FileAttr
> {
1642 run_path_with_cstr(p
, |p
| {
1644 if let Some(ret
) = unsafe { try_statx(
1647 libc
::AT_STATX_SYNC_AS_STAT
,
1654 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
1655 cvt(unsafe { stat64(p.as_ptr(), &mut stat) }
)?
;
1656 Ok(FileAttr
::from_stat64(stat
))
1660 pub fn lstat(p
: &Path
) -> io
::Result
<FileAttr
> {
1661 run_path_with_cstr(p
, |p
| {
1663 if let Some(ret
) = unsafe { try_statx(
1666 libc
::AT_SYMLINK_NOFOLLOW
| libc
::AT_STATX_SYNC_AS_STAT
,
1673 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
1674 cvt(unsafe { lstat64(p.as_ptr(), &mut stat) }
)?
;
1675 Ok(FileAttr
::from_stat64(stat
))
1679 pub fn canonicalize(p
: &Path
) -> io
::Result
<PathBuf
> {
1680 let r
= run_path_with_cstr(p
, |path
| unsafe {
1681 Ok(libc
::realpath(path
.as_ptr(), ptr
::null_mut()))
1684 return Err(io
::Error
::last_os_error());
1686 Ok(PathBuf
::from(OsString
::from_vec(unsafe {
1687 let buf
= CStr
::from_ptr(r
).to_bytes().to_vec();
1688 libc
::free(r
as *mut _
);
1693 fn open_from(from
: &Path
) -> io
::Result
<(crate::fs
::File
, crate::fs
::Metadata
)> {
1694 use crate::fs
::File
;
1695 use crate::sys_common
::fs
::NOT_FILE_ERROR
;
1697 let reader
= File
::open(from
)?
;
1698 let metadata
= reader
.metadata()?
;
1699 if !metadata
.is_file() {
1700 return Err(NOT_FILE_ERROR
);
1702 Ok((reader
, metadata
))
1705 #[cfg(target_os = "espidf")]
1706 fn open_to_and_set_permissions(
1708 reader_metadata
: crate::fs
::Metadata
,
1709 ) -> io
::Result
<(crate::fs
::File
, crate::fs
::Metadata
)> {
1710 use crate::fs
::OpenOptions
;
1711 let writer
= OpenOptions
::new().open(to
)?
;
1712 let writer_metadata
= writer
.metadata()?
;
1713 Ok((writer
, writer_metadata
))
1716 #[cfg(not(target_os = "espidf"))]
1717 fn open_to_and_set_permissions(
1719 reader_metadata
: crate::fs
::Metadata
,
1720 ) -> io
::Result
<(crate::fs
::File
, crate::fs
::Metadata
)> {
1721 use crate::fs
::OpenOptions
;
1722 use crate::os
::unix
::fs
::{OpenOptionsExt, PermissionsExt}
;
1724 let perm
= reader_metadata
.permissions();
1725 let writer
= OpenOptions
::new()
1726 // create the file with the correct mode right away
1732 let writer_metadata
= writer
.metadata()?
;
1733 // fchmod is broken on vita
1734 #[cfg(not(target_os = "vita"))]
1735 if writer_metadata
.is_file() {
1736 // Set the correct file permissions, in case the file already existed.
1737 // Don't set the permissions on already existing non-files like
1738 // pipes/FIFOs or device nodes.
1739 writer
.set_permissions(perm
)?
;
1741 Ok((writer
, writer_metadata
))
1745 target_os
= "linux",
1746 target_os
= "android",
1747 target_os
= "macos",
1750 target_os
= "watchos",
1752 pub fn copy(from
: &Path
, to
: &Path
) -> io
::Result
<u64> {
1753 let (mut reader
, reader_metadata
) = open_from(from
)?
;
1754 let (mut writer
, _
) = open_to_and_set_permissions(to
, reader_metadata
)?
;
1756 io
::copy(&mut reader
, &mut writer
)
1759 #[cfg(any(target_os = "linux", target_os = "android"))]
1760 pub fn copy(from
: &Path
, to
: &Path
) -> io
::Result
<u64> {
1761 let (mut reader
, reader_metadata
) = open_from(from
)?
;
1762 let max_len
= u64::MAX
;
1763 let (mut writer
, _
) = open_to_and_set_permissions(to
, reader_metadata
)?
;
1765 use super::kernel_copy
::{copy_regular_files, CopyResult}
;
1767 match copy_regular_files(reader
.as_raw_fd(), writer
.as_raw_fd(), max_len
) {
1768 CopyResult
::Ended(bytes
) => Ok(bytes
),
1769 CopyResult
::Error(e
, _
) => Err(e
),
1770 CopyResult
::Fallback(written
) => match io
::copy
::generic_copy(&mut reader
, &mut writer
) {
1771 Ok(bytes
) => Ok(bytes
+ written
),
1777 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
1778 pub fn copy(from
: &Path
, to
: &Path
) -> io
::Result
<u64> {
1779 use crate::sync
::atomic
::{AtomicBool, Ordering}
;
1781 const COPYFILE_ACL
: u32 = 1 << 0;
1782 const COPYFILE_STAT
: u32 = 1 << 1;
1783 const COPYFILE_XATTR
: u32 = 1 << 2;
1784 const COPYFILE_DATA
: u32 = 1 << 3;
1786 const COPYFILE_SECURITY
: u32 = COPYFILE_STAT
| COPYFILE_ACL
;
1787 const COPYFILE_METADATA
: u32 = COPYFILE_SECURITY
| COPYFILE_XATTR
;
1788 const COPYFILE_ALL
: u32 = COPYFILE_METADATA
| COPYFILE_DATA
;
1790 const COPYFILE_STATE_COPIED
: u32 = 8;
1792 #[allow(non_camel_case_types)]
1793 type copyfile_state_t
= *mut libc
::c_void
;
1794 #[allow(non_camel_case_types)]
1795 type copyfile_flags_t
= u32;
1801 state
: copyfile_state_t
,
1802 flags
: copyfile_flags_t
,
1804 fn copyfile_state_alloc() -> copyfile_state_t
;
1805 fn copyfile_state_free(state
: copyfile_state_t
) -> libc
::c_int
;
1806 fn copyfile_state_get(
1807 state
: copyfile_state_t
,
1809 dst
: *mut libc
::c_void
,
1813 struct FreeOnDrop(copyfile_state_t
);
1814 impl Drop
for FreeOnDrop
{
1815 fn drop(&mut self) {
1816 // The code below ensures that `FreeOnDrop` is never a null pointer
1818 // `copyfile_state_free` returns -1 if the `to` or `from` files
1819 // cannot be closed. However, this is not considered this an
1821 copyfile_state_free(self.0);
1826 // MacOS prior to 10.12 don't support `fclonefileat`
1827 // We store the availability in a global to avoid unnecessary syscalls
1828 static HAS_FCLONEFILEAT
: AtomicBool
= AtomicBool
::new(true);
1832 dst_dirfd
: libc
::c_int
,
1838 let (reader
, reader_metadata
) = open_from(from
)?
;
1840 // Opportunistically attempt to create a copy-on-write clone of `from`
1841 // using `fclonefileat`.
1842 if HAS_FCLONEFILEAT
.load(Ordering
::Relaxed
) {
1843 let clonefile_result
= run_path_with_cstr(to
, |to
| {
1844 cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) }
)
1846 match clonefile_result
{
1847 Ok(_
) => return Ok(reader_metadata
.len()),
1848 Err(err
) => match err
.raw_os_error() {
1849 // `fclonefileat` will fail on non-APFS volumes, if the
1850 // destination already exists, or if the source and destination
1851 // are on different devices. In all these cases `fcopyfile`
1853 Some(libc
::ENOTSUP
) | Some(libc
::EEXIST
) | Some(libc
::EXDEV
) => (),
1854 Some(libc
::ENOSYS
) => HAS_FCLONEFILEAT
.store(false, Ordering
::Relaxed
),
1855 _
=> return Err(err
),
1860 // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
1861 let (writer
, writer_metadata
) = open_to_and_set_permissions(to
, reader_metadata
)?
;
1863 // We ensure that `FreeOnDrop` never contains a null pointer so it is
1864 // always safe to call `copyfile_state_free`
1865 let state
= unsafe {
1866 let state
= copyfile_state_alloc();
1867 if state
.is_null() {
1868 return Err(crate::io
::Error
::last_os_error());
1873 let flags
= if writer_metadata
.is_file() { COPYFILE_ALL }
else { COPYFILE_DATA }
;
1875 cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) }
)?
;
1877 let mut bytes_copied
: libc
::off_t
= 0;
1881 COPYFILE_STATE_COPIED
,
1882 &mut bytes_copied
as *mut libc
::off_t
as *mut libc
::c_void
,
1885 Ok(bytes_copied
as u64)
1888 pub fn chown(path
: &Path
, uid
: u32, gid
: u32) -> io
::Result
<()> {
1889 run_path_with_cstr(path
, |path
| {
1890 cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }
)
1895 pub fn fchown(fd
: c_int
, uid
: u32, gid
: u32) -> io
::Result
<()> {
1896 cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) }
)?
;
1900 pub fn lchown(path
: &Path
, uid
: u32, gid
: u32) -> io
::Result
<()> {
1901 run_path_with_cstr(path
, |path
| {
1902 cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }
)
1907 #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
1908 pub fn chroot(dir
: &Path
) -> io
::Result
<()> {
1909 run_path_with_cstr(dir
, |dir
| cvt(unsafe { libc::chroot(dir.as_ptr()) }
).map(|_
| ()))
1912 pub use remove_dir_impl
::remove_dir_all
;
1914 // Fallback for REDOX, ESP-ID, Horizon, Vita and Miri
1916 target_os
= "redox",
1917 target_os
= "espidf",
1918 target_os
= "horizon",
1923 mod remove_dir_impl
{
1924 pub use crate::sys_common
::fs
::remove_dir_all
;
1927 // Modern implementation using openat(), unlinkat() and fdopendir()
1929 target_os
= "redox",
1930 target_os
= "espidf",
1931 target_os
= "horizon",
1936 mod remove_dir_impl
{
1937 use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir}
;
1938 use crate::ffi
::CStr
;
1940 use crate::os
::unix
::io
::{AsRawFd, FromRawFd, IntoRawFd}
;
1941 use crate::os
::unix
::prelude
::{OwnedFd, RawFd}
;
1942 use crate::path
::{Path, PathBuf}
;
1943 use crate::sys
::common
::small_c_string
::run_path_with_cstr
;
1944 use crate::sys
::{cvt, cvt_r}
;
1947 all(target_os
= "linux", target_env
= "gnu"),
1948 all(target_os
= "macos", not(target_arch
= "aarch64"))
1950 use libc
::{fdopendir, openat, unlinkat}
;
1951 #[cfg(all(target_os = "linux", target_env = "gnu"))]
1952 use libc
::{fdopendir, openat64 as openat, unlinkat}
;
1953 #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
1954 use macos_weak
::{fdopendir, openat, unlinkat}
;
1956 #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
1958 use crate::sys
::weak
::weak
;
1959 use libc
::{c_char, c_int, DIR}
;
1961 fn get_openat_fn() -> Option
<unsafe extern "C" fn(c_int
, *const c_char
, c_int
) -> c_int
> {
1962 weak
!(fn openat(c_int
, *const c_char
, c_int
) -> c_int
);
1966 pub fn has_openat() -> bool
{
1967 get_openat_fn().is_some()
1970 pub unsafe fn openat(dirfd
: c_int
, pathname
: *const c_char
, flags
: c_int
) -> c_int
{
1971 get_openat_fn().map(|openat
| openat(dirfd
, pathname
, flags
)).unwrap_or_else(|| {
1972 crate::sys
::unix
::os
::set_errno(libc
::ENOSYS
);
1977 pub unsafe fn fdopendir(fd
: c_int
) -> *mut DIR
{
1978 #[cfg(all(target_os = "macos", target_arch = "x86"))]
1979 weak
!(fn fdopendir(c_int
) -> *mut DIR
, "fdopendir$INODE64$UNIX2003");
1980 #[cfg(all(target_os = "macos", target_arch = "x86_64"))]
1981 weak
!(fn fdopendir(c_int
) -> *mut DIR
, "fdopendir$INODE64");
1982 fdopendir
.get().map(|fdopendir
| fdopendir(fd
)).unwrap_or_else(|| {
1983 crate::sys
::unix
::os
::set_errno(libc
::ENOSYS
);
1984 crate::ptr
::null_mut()
1988 pub unsafe fn unlinkat(dirfd
: c_int
, pathname
: *const c_char
, flags
: c_int
) -> c_int
{
1989 weak
!(fn unlinkat(c_int
, *const c_char
, c_int
) -> c_int
);
1990 unlinkat
.get().map(|unlinkat
| unlinkat(dirfd
, pathname
, flags
)).unwrap_or_else(|| {
1991 crate::sys
::unix
::os
::set_errno(libc
::ENOSYS
);
1997 pub fn openat_nofollow_dironly(parent_fd
: Option
<RawFd
>, p
: &CStr
) -> io
::Result
<OwnedFd
> {
1998 let fd
= cvt_r(|| unsafe {
2000 parent_fd
.unwrap_or(libc
::AT_FDCWD
),
2002 libc
::O_CLOEXEC
| libc
::O_RDONLY
| libc
::O_NOFOLLOW
| libc
::O_DIRECTORY
,
2005 Ok(unsafe { OwnedFd::from_raw_fd(fd) }
)
2008 fn fdreaddir(dir_fd
: OwnedFd
) -> io
::Result
<(ReadDir
, RawFd
)> {
2009 let ptr
= unsafe { fdopendir(dir_fd.as_raw_fd()) }
;
2011 return Err(io
::Error
::last_os_error());
2013 let dirp
= Dir(ptr
);
2014 // file descriptor is automatically closed by libc::closedir() now, so give up ownership
2015 let new_parent_fd
= dir_fd
.into_raw_fd();
2016 // a valid root is not needed because we do not call any functions involving the full path
2017 // of the `DirEntry`s.
2018 let dummy_root
= PathBuf
::new();
2019 let inner
= InnerReadDir { dirp, root: dummy_root }
;
2020 Ok((ReadDir
::new(inner
), new_parent_fd
))
2024 target_os
= "solaris",
2025 target_os
= "illumos",
2026 target_os
= "haiku",
2027 target_os
= "vxworks",
2029 fn is_dir(_ent
: &DirEntry
) -> Option
<bool
> {
2034 target_os
= "solaris",
2035 target_os
= "illumos",
2036 target_os
= "haiku",
2037 target_os
= "vxworks",
2039 fn is_dir(ent
: &DirEntry
) -> Option
<bool
> {
2040 match ent
.entry
.d_type
{
2041 libc
::DT_UNKNOWN
=> None
,
2042 libc
::DT_DIR
=> Some(true),
2047 fn remove_dir_all_recursive(parent_fd
: Option
<RawFd
>, path
: &CStr
) -> io
::Result
<()> {
2048 // try opening as directory
2049 let fd
= match openat_nofollow_dironly(parent_fd
, &path
) {
2050 Err(err
) if matches
!(err
.raw_os_error(), Some(libc
::ENOTDIR
| libc
::ELOOP
)) => {
2051 // not a directory - don't traverse further
2052 // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR)
2053 return match parent_fd
{
2055 Some(parent_fd
) => {
2056 cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }
).map(drop
)
2058 // ...unless this was supposed to be the deletion root directory
2065 // open the directory passing ownership of the fd
2066 let (dir
, fd
) = fdreaddir(fd
)?
;
2069 let child_name
= child
.name_cstr();
2070 match is_dir(&child
) {
2072 remove_dir_all_recursive(Some(fd
), child_name
)?
;
2075 cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) }
)?
;
2078 // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
2079 // if the process has the appropriate privileges. This however can causing orphaned
2080 // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
2081 // into it first instead of trying to unlink() it.
2082 remove_dir_all_recursive(Some(fd
), child_name
)?
;
2087 // unlink the directory after removing its contents
2089 unlinkat(parent_fd
.unwrap_or(libc
::AT_FDCWD
), path
.as_ptr(), libc
::AT_REMOVEDIR
)
2094 fn remove_dir_all_modern(p
: &Path
) -> io
::Result
<()> {
2095 // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
2096 // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
2098 let attr
= lstat(p
)?
;
2099 if attr
.file_type().is_symlink() {
2100 crate::fs
::remove_file(p
)
2102 run_path_with_cstr(p
, |p
| remove_dir_all_recursive(None
, &p
))
2106 #[cfg(not(all(target_os = "macos", not(target_arch = "aarch64"))))]
2107 pub fn remove_dir_all(p
: &Path
) -> io
::Result
<()> {
2108 remove_dir_all_modern(p
)
2111 #[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
2112 pub fn remove_dir_all(p
: &Path
) -> io
::Result
<()> {
2113 if macos_weak
::has_openat() {
2114 // openat() is available with macOS 10.10+, just like unlinkat() and fdopendir()
2115 remove_dir_all_modern(p
)
2117 // fall back to classic implementation
2118 crate::sys_common
::fs
::remove_dir_all(p
)