1#![allow(nonstandard_style)]
2#![allow(unsafe_op_in_unsafe_fn)]
3#![cfg_attr(miri, allow(unused))]
5
6#[cfg(test)]
7mod tests;
8
9#[cfg(all(target_os = "linux", target_env = "gnu"))]
10use libc::c_char;
11#[cfg(any(
12 all(target_os = "linux", not(target_env = "musl")),
13 target_os = "android",
14 target_os = "fuchsia",
15 target_os = "hurd",
16 target_os = "illumos",
17))]
18use libc::dirfd;
19#[cfg(any(target_os = "fuchsia", target_os = "illumos"))]
20use libc::fstatat as fstatat64;
21#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))]
22use libc::fstatat64;
23#[cfg(any(
24 target_os = "android",
25 target_os = "solaris",
26 target_os = "fuchsia",
27 target_os = "redox",
28 target_os = "illumos",
29 target_os = "aix",
30 target_os = "nto",
31 target_os = "vita",
32 all(target_os = "linux", target_env = "musl"),
33))]
34use libc::readdir as readdir64;
35#[cfg(not(any(
36 target_os = "android",
37 target_os = "linux",
38 target_os = "solaris",
39 target_os = "illumos",
40 target_os = "l4re",
41 target_os = "fuchsia",
42 target_os = "redox",
43 target_os = "aix",
44 target_os = "nto",
45 target_os = "vita",
46 target_os = "hurd",
47)))]
48use libc::readdir_r as readdir64_r;
49#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))]
50use libc::readdir64;
51#[cfg(target_os = "l4re")]
52use libc::readdir64_r;
53use libc::{c_int, mode_t};
54#[cfg(target_os = "android")]
55use libc::{
56 dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64,
57 lstat as lstat64, off64_t, open as open64, stat as stat64,
58};
59#[cfg(not(any(
60 all(target_os = "linux", not(target_env = "musl")),
61 target_os = "l4re",
62 target_os = "android",
63 target_os = "hurd",
64)))]
65use libc::{
66 dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64,
67 lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
68};
69#[cfg(any(
70 all(target_os = "linux", not(target_env = "musl")),
71 target_os = "l4re",
72 target_os = "hurd"
73))]
74use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64};
75
76use crate::ffi::{CStr, OsStr, OsString};
77use crate::fmt::{self, Write as _};
78use crate::fs::TryLockError;
79use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
80use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
81use crate::os::unix::prelude::*;
82use crate::path::{Path, PathBuf};
83use crate::sync::Arc;
84use crate::sys::common::small_c_string::run_path_with_cstr;
85use crate::sys::fd::FileDesc;
86pub use crate::sys::fs::common::exists;
87use crate::sys::time::SystemTime;
88#[cfg(all(target_os = "linux", target_env = "gnu"))]
89use crate::sys::weak::syscall;
90#[cfg(target_os = "android")]
91use crate::sys::weak::weak;
92use crate::sys::{cvt, cvt_r};
93use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
94use crate::{mem, ptr};
95
96pub struct File(FileDesc);
97
98macro_rules! cfg_has_statx {
103 ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
104 cfg_if::cfg_if! {
105 if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
106 $($then_tt)*
107 } else {
108 $($else_tt)*
109 }
110 }
111 };
112 ($($block_inner:tt)*) => {
113 #[cfg(all(target_os = "linux", target_env = "gnu"))]
114 {
115 $($block_inner)*
116 }
117 };
118}
119
120cfg_has_statx! {{
121 #[derive(Clone)]
122 pub struct FileAttr {
123 stat: stat64,
124 statx_extra_fields: Option<StatxExtraFields>,
125 }
126
127 #[derive(Clone)]
128 struct StatxExtraFields {
129 stx_mask: u32,
131 stx_btime: libc::statx_timestamp,
132 #[cfg(target_pointer_width = "32")]
134 stx_atime: libc::statx_timestamp,
135 #[cfg(target_pointer_width = "32")]
136 stx_ctime: libc::statx_timestamp,
137 #[cfg(target_pointer_width = "32")]
138 stx_mtime: libc::statx_timestamp,
139
140 }
141
142 unsafe fn try_statx(
146 fd: c_int,
147 path: *const c_char,
148 flags: i32,
149 mask: u32,
150 ) -> Option<io::Result<FileAttr>> {
151 use crate::sync::atomic::{Atomic, AtomicU8, Ordering};
152
153 #[repr(u8)]
157 enum STATX_STATE{ Unknown = 0, Present, Unavailable }
158 static STATX_SAVED_STATE: Atomic<u8> = AtomicU8::new(STATX_STATE::Unknown as u8);
159
160 syscall!(
161 fn statx(
162 fd: c_int,
163 pathname: *const c_char,
164 flags: c_int,
165 mask: libc::c_uint,
166 statxbuf: *mut libc::statx,
167 ) -> c_int;
168 );
169
170 let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed);
171 if statx_availability == STATX_STATE::Unavailable as u8 {
172 return None;
173 }
174
175 let mut buf: libc::statx = mem::zeroed();
176 if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
177 if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 {
178 return Some(Err(err));
179 }
180
181 let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, ptr::null_mut()))
193 .err()
194 .and_then(|e| e.raw_os_error());
195 if err2 == Some(libc::EFAULT) {
196 STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
197 return Some(Err(err));
198 } else {
199 STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
200 return None;
201 }
202 }
203 if statx_availability == STATX_STATE::Unknown as u8 {
204 STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
205 }
206
207 let mut stat: stat64 = mem::zeroed();
209 stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
211 stat.st_ino = buf.stx_ino as libc::ino64_t;
212 stat.st_nlink = buf.stx_nlink as libc::nlink_t;
213 stat.st_mode = buf.stx_mode as libc::mode_t;
214 stat.st_uid = buf.stx_uid as libc::uid_t;
215 stat.st_gid = buf.stx_gid as libc::gid_t;
216 stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
217 stat.st_size = buf.stx_size as off64_t;
218 stat.st_blksize = buf.stx_blksize as libc::blksize_t;
219 stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
220 stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
221 stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
223 stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
224 stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
225 stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
226 stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
227
228 let extra = StatxExtraFields {
229 stx_mask: buf.stx_mask,
230 stx_btime: buf.stx_btime,
231 #[cfg(target_pointer_width = "32")]
233 stx_atime: buf.stx_atime,
234 #[cfg(target_pointer_width = "32")]
235 stx_ctime: buf.stx_ctime,
236 #[cfg(target_pointer_width = "32")]
237 stx_mtime: buf.stx_mtime,
238 };
239
240 Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
241 }
242
243} else {
244 #[derive(Clone)]
245 pub struct FileAttr {
246 stat: stat64,
247 }
248}}
249
250struct InnerReadDir {
252 dirp: Dir,
253 root: PathBuf,
254}
255
256pub struct ReadDir {
257 inner: Arc<InnerReadDir>,
258 end_of_stream: bool,
259}
260
261impl ReadDir {
262 fn new(inner: InnerReadDir) -> Self {
263 Self { inner: Arc::new(inner), end_of_stream: false }
264 }
265}
266
267struct Dir(*mut libc::DIR);
268
269unsafe impl Send for Dir {}
270unsafe impl Sync for Dir {}
271
272#[cfg(any(
273 target_os = "android",
274 target_os = "linux",
275 target_os = "solaris",
276 target_os = "illumos",
277 target_os = "fuchsia",
278 target_os = "redox",
279 target_os = "aix",
280 target_os = "nto",
281 target_os = "vita",
282 target_os = "hurd",
283))]
284pub struct DirEntry {
285 dir: Arc<InnerReadDir>,
286 entry: dirent64_min,
287 name: crate::ffi::CString,
291}
292
293#[cfg(any(
297 target_os = "android",
298 target_os = "linux",
299 target_os = "solaris",
300 target_os = "illumos",
301 target_os = "fuchsia",
302 target_os = "redox",
303 target_os = "aix",
304 target_os = "nto",
305 target_os = "vita",
306 target_os = "hurd",
307))]
308struct dirent64_min {
309 d_ino: u64,
310 #[cfg(not(any(
311 target_os = "solaris",
312 target_os = "illumos",
313 target_os = "aix",
314 target_os = "nto",
315 target_os = "vita",
316 )))]
317 d_type: u8,
318}
319
320#[cfg(not(any(
321 target_os = "android",
322 target_os = "linux",
323 target_os = "solaris",
324 target_os = "illumos",
325 target_os = "fuchsia",
326 target_os = "redox",
327 target_os = "aix",
328 target_os = "nto",
329 target_os = "vita",
330 target_os = "hurd",
331)))]
332pub struct DirEntry {
333 dir: Arc<InnerReadDir>,
334 entry: dirent64,
336}
337
338#[derive(Clone)]
339pub struct OpenOptions {
340 read: bool,
342 write: bool,
343 append: bool,
344 truncate: bool,
345 create: bool,
346 create_new: bool,
347 custom_flags: i32,
349 mode: mode_t,
350}
351
352#[derive(Clone, PartialEq, Eq)]
353pub struct FilePermissions {
354 mode: mode_t,
355}
356
357#[derive(Copy, Clone, Debug, Default)]
358pub struct FileTimes {
359 accessed: Option<SystemTime>,
360 modified: Option<SystemTime>,
361 #[cfg(target_vendor = "apple")]
362 created: Option<SystemTime>,
363}
364
365#[derive(Copy, Clone, Eq)]
366pub struct FileType {
367 mode: mode_t,
368}
369
370impl PartialEq for FileType {
371 fn eq(&self, other: &Self) -> bool {
372 self.masked() == other.masked()
373 }
374}
375
376impl core::hash::Hash for FileType {
377 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
378 self.masked().hash(state);
379 }
380}
381
382pub struct DirBuilder {
383 mode: mode_t,
384}
385
386#[derive(Copy, Clone)]
387struct Mode(mode_t);
388
389cfg_has_statx! {{
390 impl FileAttr {
391 fn from_stat64(stat: stat64) -> Self {
392 Self { stat, statx_extra_fields: None }
393 }
394
395 #[cfg(target_pointer_width = "32")]
396 pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> {
397 if let Some(ext) = &self.statx_extra_fields {
398 if (ext.stx_mask & libc::STATX_MTIME) != 0 {
399 return Some(&ext.stx_mtime);
400 }
401 }
402 None
403 }
404
405 #[cfg(target_pointer_width = "32")]
406 pub fn stx_atime(&self) -> Option<&libc::statx_timestamp> {
407 if let Some(ext) = &self.statx_extra_fields {
408 if (ext.stx_mask & libc::STATX_ATIME) != 0 {
409 return Some(&ext.stx_atime);
410 }
411 }
412 None
413 }
414
415 #[cfg(target_pointer_width = "32")]
416 pub fn stx_ctime(&self) -> Option<&libc::statx_timestamp> {
417 if let Some(ext) = &self.statx_extra_fields {
418 if (ext.stx_mask & libc::STATX_CTIME) != 0 {
419 return Some(&ext.stx_ctime);
420 }
421 }
422 None
423 }
424 }
425} else {
426 impl FileAttr {
427 fn from_stat64(stat: stat64) -> Self {
428 Self { stat }
429 }
430 }
431}}
432
433impl FileAttr {
434 pub fn size(&self) -> u64 {
435 self.stat.st_size as u64
436 }
437 pub fn perm(&self) -> FilePermissions {
438 FilePermissions { mode: (self.stat.st_mode as mode_t) }
439 }
440
441 pub fn file_type(&self) -> FileType {
442 FileType { mode: self.stat.st_mode as mode_t }
443 }
444}
445
446#[cfg(target_os = "netbsd")]
447impl FileAttr {
448 pub fn modified(&self) -> io::Result<SystemTime> {
449 SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64)
450 }
451
452 pub fn accessed(&self) -> io::Result<SystemTime> {
453 SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64)
454 }
455
456 pub fn created(&self) -> io::Result<SystemTime> {
457 SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64)
458 }
459}
460
461#[cfg(target_os = "aix")]
462impl FileAttr {
463 pub fn modified(&self) -> io::Result<SystemTime> {
464 SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64)
465 }
466
467 pub fn accessed(&self) -> io::Result<SystemTime> {
468 SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64)
469 }
470
471 pub fn created(&self) -> io::Result<SystemTime> {
472 SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64)
473 }
474}
475
476#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))]
477impl FileAttr {
478 #[cfg(not(any(
479 target_os = "vxworks",
480 target_os = "espidf",
481 target_os = "horizon",
482 target_os = "vita",
483 target_os = "hurd",
484 target_os = "rtems",
485 target_os = "nuttx",
486 )))]
487 pub fn modified(&self) -> io::Result<SystemTime> {
488 #[cfg(target_pointer_width = "32")]
489 cfg_has_statx! {
490 if let Some(mtime) = self.stx_mtime() {
491 return SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64);
492 }
493 }
494
495 SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64)
496 }
497
498 #[cfg(any(
499 target_os = "vxworks",
500 target_os = "espidf",
501 target_os = "vita",
502 target_os = "rtems",
503 ))]
504 pub fn modified(&self) -> io::Result<SystemTime> {
505 SystemTime::new(self.stat.st_mtime as i64, 0)
506 }
507
508 #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))]
509 pub fn modified(&self) -> io::Result<SystemTime> {
510 SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64)
511 }
512
513 #[cfg(not(any(
514 target_os = "vxworks",
515 target_os = "espidf",
516 target_os = "horizon",
517 target_os = "vita",
518 target_os = "hurd",
519 target_os = "rtems",
520 target_os = "nuttx",
521 )))]
522 pub fn accessed(&self) -> io::Result<SystemTime> {
523 #[cfg(target_pointer_width = "32")]
524 cfg_has_statx! {
525 if let Some(atime) = self.stx_atime() {
526 return SystemTime::new(atime.tv_sec, atime.tv_nsec as i64);
527 }
528 }
529
530 SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64)
531 }
532
533 #[cfg(any(
534 target_os = "vxworks",
535 target_os = "espidf",
536 target_os = "vita",
537 target_os = "rtems"
538 ))]
539 pub fn accessed(&self) -> io::Result<SystemTime> {
540 SystemTime::new(self.stat.st_atime as i64, 0)
541 }
542
543 #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))]
544 pub fn accessed(&self) -> io::Result<SystemTime> {
545 SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64)
546 }
547
548 #[cfg(any(
549 target_os = "freebsd",
550 target_os = "openbsd",
551 target_vendor = "apple",
552 target_os = "cygwin",
553 ))]
554 pub fn created(&self) -> io::Result<SystemTime> {
555 SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64)
556 }
557
558 #[cfg(not(any(
559 target_os = "freebsd",
560 target_os = "openbsd",
561 target_os = "vita",
562 target_vendor = "apple",
563 target_os = "cygwin",
564 )))]
565 pub fn created(&self) -> io::Result<SystemTime> {
566 cfg_has_statx! {
567 if let Some(ext) = &self.statx_extra_fields {
568 return if (ext.stx_mask & libc::STATX_BTIME) != 0 {
569 SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64)
570 } else {
571 Err(io::const_error!(
572 io::ErrorKind::Unsupported,
573 "creation time is not available for the filesystem",
574 ))
575 };
576 }
577 }
578
579 Err(io::const_error!(
580 io::ErrorKind::Unsupported,
581 "creation time is not available on this platform currently",
582 ))
583 }
584
585 #[cfg(target_os = "vita")]
586 pub fn created(&self) -> io::Result<SystemTime> {
587 SystemTime::new(self.stat.st_ctime as i64, 0)
588 }
589}
590
591#[cfg(target_os = "nto")]
592impl FileAttr {
593 pub fn modified(&self) -> io::Result<SystemTime> {
594 SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec)
595 }
596
597 pub fn accessed(&self) -> io::Result<SystemTime> {
598 SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec)
599 }
600
601 pub fn created(&self) -> io::Result<SystemTime> {
602 SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec)
603 }
604}
605
606impl AsInner<stat64> for FileAttr {
607 #[inline]
608 fn as_inner(&self) -> &stat64 {
609 &self.stat
610 }
611}
612
613impl FilePermissions {
614 pub fn readonly(&self) -> bool {
615 self.mode & 0o222 == 0
617 }
618
619 pub fn set_readonly(&mut self, readonly: bool) {
620 if readonly {
621 self.mode &= !0o222;
623 } else {
624 self.mode |= 0o222;
626 }
627 }
628 pub fn mode(&self) -> u32 {
629 self.mode as u32
630 }
631}
632
633impl FileTimes {
634 pub fn set_accessed(&mut self, t: SystemTime) {
635 self.accessed = Some(t);
636 }
637
638 pub fn set_modified(&mut self, t: SystemTime) {
639 self.modified = Some(t);
640 }
641
642 #[cfg(target_vendor = "apple")]
643 pub fn set_created(&mut self, t: SystemTime) {
644 self.created = Some(t);
645 }
646}
647
648impl FileType {
649 pub fn is_dir(&self) -> bool {
650 self.is(libc::S_IFDIR)
651 }
652 pub fn is_file(&self) -> bool {
653 self.is(libc::S_IFREG)
654 }
655 pub fn is_symlink(&self) -> bool {
656 self.is(libc::S_IFLNK)
657 }
658
659 pub fn is(&self, mode: mode_t) -> bool {
660 self.masked() == mode
661 }
662
663 fn masked(&self) -> mode_t {
664 self.mode & libc::S_IFMT
665 }
666}
667
668impl fmt::Debug for FileType {
669 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670 let FileType { mode } = self;
671 f.debug_struct("FileType").field("mode", &Mode(*mode)).finish()
672 }
673}
674
675impl FromInner<u32> for FilePermissions {
676 fn from_inner(mode: u32) -> FilePermissions {
677 FilePermissions { mode: mode as mode_t }
678 }
679}
680
681impl fmt::Debug for FilePermissions {
682 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683 let FilePermissions { mode } = self;
684 f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish()
685 }
686}
687
688impl fmt::Debug for ReadDir {
689 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690 fmt::Debug::fmt(&*self.inner.root, f)
693 }
694}
695
696impl Iterator for ReadDir {
697 type Item = io::Result<DirEntry>;
698
699 #[cfg(any(
700 target_os = "android",
701 target_os = "linux",
702 target_os = "solaris",
703 target_os = "fuchsia",
704 target_os = "redox",
705 target_os = "illumos",
706 target_os = "aix",
707 target_os = "nto",
708 target_os = "vita",
709 target_os = "hurd",
710 ))]
711 fn next(&mut self) -> Option<io::Result<DirEntry>> {
712 use crate::sys::os::{errno, set_errno};
713
714 if self.end_of_stream {
715 return None;
716 }
717
718 unsafe {
719 loop {
720 set_errno(0);
726 let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0);
727 if entry_ptr.is_null() {
728 self.end_of_stream = true;
731
732 return match errno() {
735 0 => None,
736 e => Some(Err(Error::from_raw_os_error(e))),
737 };
738 }
739
740 let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast());
760 let name_bytes = name.to_bytes();
761 if name_bytes == b"." || name_bytes == b".." {
762 continue;
763 }
764
765 #[cfg(not(target_os = "vita"))]
769 let entry = dirent64_min {
770 d_ino: (*entry_ptr).d_ino as u64,
771 #[cfg(not(any(
772 target_os = "solaris",
773 target_os = "illumos",
774 target_os = "aix",
775 target_os = "nto",
776 )))]
777 d_type: (*entry_ptr).d_type as u8,
778 };
779
780 #[cfg(target_os = "vita")]
781 let entry = dirent64_min { d_ino: 0u64 };
782
783 return Some(Ok(DirEntry {
784 entry,
785 name: name.to_owned(),
786 dir: Arc::clone(&self.inner),
787 }));
788 }
789 }
790 }
791
792 #[cfg(not(any(
793 target_os = "android",
794 target_os = "linux",
795 target_os = "solaris",
796 target_os = "fuchsia",
797 target_os = "redox",
798 target_os = "illumos",
799 target_os = "aix",
800 target_os = "nto",
801 target_os = "vita",
802 target_os = "hurd",
803 )))]
804 fn next(&mut self) -> Option<io::Result<DirEntry>> {
805 if self.end_of_stream {
806 return None;
807 }
808
809 unsafe {
810 let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
811 let mut entry_ptr = ptr::null_mut();
812 loop {
813 let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr);
814 if err != 0 {
815 if entry_ptr.is_null() {
816 self.end_of_stream = true;
821 }
822 return Some(Err(Error::from_raw_os_error(err)));
823 }
824 if entry_ptr.is_null() {
825 return None;
826 }
827 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
828 return Some(Ok(ret));
829 }
830 }
831 }
832 }
833}
834
835#[inline]
844pub(crate) fn debug_assert_fd_is_open(fd: RawFd) {
845 use crate::sys::os::errno;
846
847 if core::ub_checks::check_library_ub() {
849 if unsafe { libc::fcntl(fd, libc::F_GETFD) } == -1 && errno() == libc::EBADF {
850 rtabort!("IO Safety violation: owned file descriptor already closed");
851 }
852 }
853}
854
855impl Drop for Dir {
856 fn drop(&mut self) {
857 #[cfg(not(any(
859 miri,
860 target_os = "redox",
861 target_os = "nto",
862 target_os = "vita",
863 target_os = "hurd",
864 target_os = "espidf",
865 target_os = "horizon",
866 target_os = "vxworks",
867 target_os = "rtems",
868 target_os = "nuttx",
869 )))]
870 {
871 let fd = unsafe { libc::dirfd(self.0) };
872 debug_assert_fd_is_open(fd);
873 }
874 let r = unsafe { libc::closedir(self.0) };
875 assert!(
876 r == 0 || crate::io::Error::last_os_error().is_interrupted(),
877 "unexpected error during closedir: {:?}",
878 crate::io::Error::last_os_error()
879 );
880 }
881}
882
883impl DirEntry {
884 pub fn path(&self) -> PathBuf {
885 self.dir.root.join(self.file_name_os_str())
886 }
887
888 pub fn file_name(&self) -> OsString {
889 self.file_name_os_str().to_os_string()
890 }
891
892 #[cfg(all(
893 any(
894 all(target_os = "linux", not(target_env = "musl")),
895 target_os = "android",
896 target_os = "fuchsia",
897 target_os = "hurd",
898 target_os = "illumos",
899 ),
900 not(miri) ))]
902 pub fn metadata(&self) -> io::Result<FileAttr> {
903 let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
904 let name = self.name_cstr().as_ptr();
905
906 cfg_has_statx! {
907 if let Some(ret) = unsafe { try_statx(
908 fd,
909 name,
910 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
911 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
912 ) } {
913 return ret;
914 }
915 }
916
917 let mut stat: stat64 = unsafe { mem::zeroed() };
918 cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
919 Ok(FileAttr::from_stat64(stat))
920 }
921
922 #[cfg(any(
923 not(any(
924 all(target_os = "linux", not(target_env = "musl")),
925 target_os = "android",
926 target_os = "fuchsia",
927 target_os = "hurd",
928 target_os = "illumos",
929 )),
930 miri
931 ))]
932 pub fn metadata(&self) -> io::Result<FileAttr> {
933 run_path_with_cstr(&self.path(), &lstat)
934 }
935
936 #[cfg(any(
937 target_os = "solaris",
938 target_os = "illumos",
939 target_os = "haiku",
940 target_os = "vxworks",
941 target_os = "aix",
942 target_os = "nto",
943 target_os = "vita",
944 ))]
945 pub fn file_type(&self) -> io::Result<FileType> {
946 self.metadata().map(|m| m.file_type())
947 }
948
949 #[cfg(not(any(
950 target_os = "solaris",
951 target_os = "illumos",
952 target_os = "haiku",
953 target_os = "vxworks",
954 target_os = "aix",
955 target_os = "nto",
956 target_os = "vita",
957 )))]
958 pub fn file_type(&self) -> io::Result<FileType> {
959 match self.entry.d_type {
960 libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
961 libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
962 libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
963 libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
964 libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
965 libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
966 libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
967 _ => self.metadata().map(|m| m.file_type()),
968 }
969 }
970
971 #[cfg(any(
972 target_os = "linux",
973 target_os = "cygwin",
974 target_os = "emscripten",
975 target_os = "android",
976 target_os = "solaris",
977 target_os = "illumos",
978 target_os = "haiku",
979 target_os = "l4re",
980 target_os = "fuchsia",
981 target_os = "redox",
982 target_os = "vxworks",
983 target_os = "espidf",
984 target_os = "horizon",
985 target_os = "vita",
986 target_os = "aix",
987 target_os = "nto",
988 target_os = "hurd",
989 target_os = "rtems",
990 target_vendor = "apple",
991 ))]
992 pub fn ino(&self) -> u64 {
993 self.entry.d_ino as u64
994 }
995
996 #[cfg(any(
997 target_os = "freebsd",
998 target_os = "openbsd",
999 target_os = "netbsd",
1000 target_os = "dragonfly"
1001 ))]
1002 pub fn ino(&self) -> u64 {
1003 self.entry.d_fileno as u64
1004 }
1005
1006 #[cfg(target_os = "nuttx")]
1007 pub fn ino(&self) -> u64 {
1008 0
1011 }
1012
1013 #[cfg(any(
1014 target_os = "netbsd",
1015 target_os = "openbsd",
1016 target_os = "freebsd",
1017 target_os = "dragonfly",
1018 target_vendor = "apple",
1019 ))]
1020 fn name_bytes(&self) -> &[u8] {
1021 use crate::slice;
1022 unsafe {
1023 slice::from_raw_parts(
1024 self.entry.d_name.as_ptr() as *const u8,
1025 self.entry.d_namlen as usize,
1026 )
1027 }
1028 }
1029 #[cfg(not(any(
1030 target_os = "netbsd",
1031 target_os = "openbsd",
1032 target_os = "freebsd",
1033 target_os = "dragonfly",
1034 target_vendor = "apple",
1035 )))]
1036 fn name_bytes(&self) -> &[u8] {
1037 self.name_cstr().to_bytes()
1038 }
1039
1040 #[cfg(not(any(
1041 target_os = "android",
1042 target_os = "linux",
1043 target_os = "solaris",
1044 target_os = "illumos",
1045 target_os = "fuchsia",
1046 target_os = "redox",
1047 target_os = "aix",
1048 target_os = "nto",
1049 target_os = "vita",
1050 target_os = "hurd",
1051 )))]
1052 fn name_cstr(&self) -> &CStr {
1053 unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
1054 }
1055 #[cfg(any(
1056 target_os = "android",
1057 target_os = "linux",
1058 target_os = "solaris",
1059 target_os = "illumos",
1060 target_os = "fuchsia",
1061 target_os = "redox",
1062 target_os = "aix",
1063 target_os = "nto",
1064 target_os = "vita",
1065 target_os = "hurd",
1066 ))]
1067 fn name_cstr(&self) -> &CStr {
1068 &self.name
1069 }
1070
1071 pub fn file_name_os_str(&self) -> &OsStr {
1072 OsStr::from_bytes(self.name_bytes())
1073 }
1074}
1075
1076impl OpenOptions {
1077 pub fn new() -> OpenOptions {
1078 OpenOptions {
1079 read: false,
1081 write: false,
1082 append: false,
1083 truncate: false,
1084 create: false,
1085 create_new: false,
1086 custom_flags: 0,
1088 mode: 0o666,
1089 }
1090 }
1091
1092 pub fn read(&mut self, read: bool) {
1093 self.read = read;
1094 }
1095 pub fn write(&mut self, write: bool) {
1096 self.write = write;
1097 }
1098 pub fn append(&mut self, append: bool) {
1099 self.append = append;
1100 }
1101 pub fn truncate(&mut self, truncate: bool) {
1102 self.truncate = truncate;
1103 }
1104 pub fn create(&mut self, create: bool) {
1105 self.create = create;
1106 }
1107 pub fn create_new(&mut self, create_new: bool) {
1108 self.create_new = create_new;
1109 }
1110
1111 pub fn custom_flags(&mut self, flags: i32) {
1112 self.custom_flags = flags;
1113 }
1114 pub fn mode(&mut self, mode: u32) {
1115 self.mode = mode as mode_t;
1116 }
1117
1118 fn get_access_mode(&self) -> io::Result<c_int> {
1119 match (self.read, self.write, self.append) {
1120 (true, false, false) => Ok(libc::O_RDONLY),
1121 (false, true, false) => Ok(libc::O_WRONLY),
1122 (true, true, false) => Ok(libc::O_RDWR),
1123 (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
1124 (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
1125 (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
1126 }
1127 }
1128
1129 fn get_creation_mode(&self) -> io::Result<c_int> {
1130 match (self.write, self.append) {
1131 (true, false) => {}
1132 (false, false) => {
1133 if self.truncate || self.create || self.create_new {
1134 return Err(Error::from_raw_os_error(libc::EINVAL));
1135 }
1136 }
1137 (_, true) => {
1138 if self.truncate && !self.create_new {
1139 return Err(Error::from_raw_os_error(libc::EINVAL));
1140 }
1141 }
1142 }
1143
1144 Ok(match (self.create, self.truncate, self.create_new) {
1145 (false, false, false) => 0,
1146 (true, false, false) => libc::O_CREAT,
1147 (false, true, false) => libc::O_TRUNC,
1148 (true, true, false) => libc::O_CREAT | libc::O_TRUNC,
1149 (_, _, true) => libc::O_CREAT | libc::O_EXCL,
1150 })
1151 }
1152}
1153
1154impl fmt::Debug for OpenOptions {
1155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1156 let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } =
1157 self;
1158 f.debug_struct("OpenOptions")
1159 .field("read", read)
1160 .field("write", write)
1161 .field("append", append)
1162 .field("truncate", truncate)
1163 .field("create", create)
1164 .field("create_new", create_new)
1165 .field("custom_flags", custom_flags)
1166 .field("mode", &Mode(*mode))
1167 .finish()
1168 }
1169}
1170
1171impl File {
1172 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
1173 run_path_with_cstr(path, &|path| File::open_c(path, opts))
1174 }
1175
1176 pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
1177 let flags = libc::O_CLOEXEC
1178 | opts.get_access_mode()?
1179 | opts.get_creation_mode()?
1180 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
1181 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
1186 Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
1187 }
1188
1189 pub fn file_attr(&self) -> io::Result<FileAttr> {
1190 let fd = self.as_raw_fd();
1191
1192 cfg_has_statx! {
1193 if let Some(ret) = unsafe { try_statx(
1194 fd,
1195 c"".as_ptr() as *const c_char,
1196 libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
1197 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
1198 ) } {
1199 return ret;
1200 }
1201 }
1202
1203 let mut stat: stat64 = unsafe { mem::zeroed() };
1204 cvt(unsafe { fstat64(fd, &mut stat) })?;
1205 Ok(FileAttr::from_stat64(stat))
1206 }
1207
1208 pub fn fsync(&self) -> io::Result<()> {
1209 cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
1210 return Ok(());
1211
1212 #[cfg(target_vendor = "apple")]
1213 unsafe fn os_fsync(fd: c_int) -> c_int {
1214 libc::fcntl(fd, libc::F_FULLFSYNC)
1215 }
1216 #[cfg(not(target_vendor = "apple"))]
1217 unsafe fn os_fsync(fd: c_int) -> c_int {
1218 libc::fsync(fd)
1219 }
1220 }
1221
1222 pub fn datasync(&self) -> io::Result<()> {
1223 cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
1224 return Ok(());
1225
1226 #[cfg(target_vendor = "apple")]
1227 unsafe fn os_datasync(fd: c_int) -> c_int {
1228 libc::fcntl(fd, libc::F_FULLFSYNC)
1229 }
1230 #[cfg(any(
1231 target_os = "freebsd",
1232 target_os = "fuchsia",
1233 target_os = "linux",
1234 target_os = "cygwin",
1235 target_os = "android",
1236 target_os = "netbsd",
1237 target_os = "openbsd",
1238 target_os = "nto",
1239 target_os = "hurd",
1240 ))]
1241 unsafe fn os_datasync(fd: c_int) -> c_int {
1242 libc::fdatasync(fd)
1243 }
1244 #[cfg(not(any(
1245 target_os = "android",
1246 target_os = "fuchsia",
1247 target_os = "freebsd",
1248 target_os = "linux",
1249 target_os = "cygwin",
1250 target_os = "netbsd",
1251 target_os = "openbsd",
1252 target_os = "nto",
1253 target_os = "hurd",
1254 target_vendor = "apple",
1255 )))]
1256 unsafe fn os_datasync(fd: c_int) -> c_int {
1257 libc::fsync(fd)
1258 }
1259 }
1260
1261 #[cfg(any(
1262 target_os = "freebsd",
1263 target_os = "fuchsia",
1264 target_os = "linux",
1265 target_os = "netbsd",
1266 target_os = "openbsd",
1267 target_vendor = "apple",
1268 ))]
1269 pub fn lock(&self) -> io::Result<()> {
1270 cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?;
1271 return Ok(());
1272 }
1273
1274 #[cfg(not(any(
1275 target_os = "freebsd",
1276 target_os = "fuchsia",
1277 target_os = "linux",
1278 target_os = "netbsd",
1279 target_os = "openbsd",
1280 target_vendor = "apple",
1281 )))]
1282 pub fn lock(&self) -> io::Result<()> {
1283 Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported"))
1284 }
1285
1286 #[cfg(any(
1287 target_os = "freebsd",
1288 target_os = "fuchsia",
1289 target_os = "linux",
1290 target_os = "netbsd",
1291 target_os = "openbsd",
1292 target_vendor = "apple",
1293 ))]
1294 pub fn lock_shared(&self) -> io::Result<()> {
1295 cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?;
1296 return Ok(());
1297 }
1298
1299 #[cfg(not(any(
1300 target_os = "freebsd",
1301 target_os = "fuchsia",
1302 target_os = "linux",
1303 target_os = "netbsd",
1304 target_os = "openbsd",
1305 target_vendor = "apple",
1306 )))]
1307 pub fn lock_shared(&self) -> io::Result<()> {
1308 Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported"))
1309 }
1310
1311 #[cfg(any(
1312 target_os = "freebsd",
1313 target_os = "fuchsia",
1314 target_os = "linux",
1315 target_os = "netbsd",
1316 target_os = "openbsd",
1317 target_vendor = "apple",
1318 ))]
1319 pub fn try_lock(&self) -> Result<(), TryLockError> {
1320 let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) });
1321 if let Err(err) = result {
1322 if err.kind() == io::ErrorKind::WouldBlock {
1323 Err(TryLockError::WouldBlock)
1324 } else {
1325 Err(TryLockError::Error(err))
1326 }
1327 } else {
1328 Ok(())
1329 }
1330 }
1331
1332 #[cfg(not(any(
1333 target_os = "freebsd",
1334 target_os = "fuchsia",
1335 target_os = "linux",
1336 target_os = "netbsd",
1337 target_os = "openbsd",
1338 target_vendor = "apple",
1339 )))]
1340 pub fn try_lock(&self) -> Result<(), TryLockError> {
1341 Err(TryLockError::Error(io::const_error!(
1342 io::ErrorKind::Unsupported,
1343 "try_lock() not supported"
1344 )))
1345 }
1346
1347 #[cfg(any(
1348 target_os = "freebsd",
1349 target_os = "fuchsia",
1350 target_os = "linux",
1351 target_os = "netbsd",
1352 target_os = "openbsd",
1353 target_vendor = "apple",
1354 ))]
1355 pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
1356 let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) });
1357 if let Err(err) = result {
1358 if err.kind() == io::ErrorKind::WouldBlock {
1359 Err(TryLockError::WouldBlock)
1360 } else {
1361 Err(TryLockError::Error(err))
1362 }
1363 } else {
1364 Ok(())
1365 }
1366 }
1367
1368 #[cfg(not(any(
1369 target_os = "freebsd",
1370 target_os = "fuchsia",
1371 target_os = "linux",
1372 target_os = "netbsd",
1373 target_os = "openbsd",
1374 target_vendor = "apple",
1375 )))]
1376 pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
1377 Err(TryLockError::Error(io::const_error!(
1378 io::ErrorKind::Unsupported,
1379 "try_lock_shared() not supported"
1380 )))
1381 }
1382
1383 #[cfg(any(
1384 target_os = "freebsd",
1385 target_os = "fuchsia",
1386 target_os = "linux",
1387 target_os = "netbsd",
1388 target_os = "openbsd",
1389 target_vendor = "apple",
1390 ))]
1391 pub fn unlock(&self) -> io::Result<()> {
1392 cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?;
1393 return Ok(());
1394 }
1395
1396 #[cfg(not(any(
1397 target_os = "freebsd",
1398 target_os = "fuchsia",
1399 target_os = "linux",
1400 target_os = "netbsd",
1401 target_os = "openbsd",
1402 target_vendor = "apple",
1403 )))]
1404 pub fn unlock(&self) -> io::Result<()> {
1405 Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported"))
1406 }
1407
1408 pub fn truncate(&self, size: u64) -> io::Result<()> {
1409 let size: off64_t =
1410 size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1411 cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
1412 }
1413
1414 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
1415 self.0.read(buf)
1416 }
1417
1418 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
1419 self.0.read_vectored(bufs)
1420 }
1421
1422 #[inline]
1423 pub fn is_read_vectored(&self) -> bool {
1424 self.0.is_read_vectored()
1425 }
1426
1427 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
1428 self.0.read_at(buf, offset)
1429 }
1430
1431 pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
1432 self.0.read_buf(cursor)
1433 }
1434
1435 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
1436 self.0.read_vectored_at(bufs, offset)
1437 }
1438
1439 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
1440 self.0.write(buf)
1441 }
1442
1443 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
1444 self.0.write_vectored(bufs)
1445 }
1446
1447 #[inline]
1448 pub fn is_write_vectored(&self) -> bool {
1449 self.0.is_write_vectored()
1450 }
1451
1452 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
1453 self.0.write_at(buf, offset)
1454 }
1455
1456 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
1457 self.0.write_vectored_at(bufs, offset)
1458 }
1459
1460 #[inline]
1461 pub fn flush(&self) -> io::Result<()> {
1462 Ok(())
1463 }
1464
1465 pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
1466 let (whence, pos) = match pos {
1467 SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
1470 SeekFrom::End(off) => (libc::SEEK_END, off),
1471 SeekFrom::Current(off) => (libc::SEEK_CUR, off),
1472 };
1473 let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) })?;
1474 Ok(n as u64)
1475 }
1476
1477 pub fn size(&self) -> Option<io::Result<u64>> {
1478 match self.file_attr().map(|attr| attr.size()) {
1479 Ok(0) => None,
1482 result => Some(result),
1483 }
1484 }
1485
1486 pub fn tell(&self) -> io::Result<u64> {
1487 self.seek(SeekFrom::Current(0))
1488 }
1489
1490 pub fn duplicate(&self) -> io::Result<File> {
1491 self.0.duplicate().map(File)
1492 }
1493
1494 pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
1495 cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
1496 Ok(())
1497 }
1498
1499 pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
1500 #[cfg(not(any(
1501 target_os = "redox",
1502 target_os = "espidf",
1503 target_os = "horizon",
1504 target_os = "nuttx",
1505 )))]
1506 let to_timespec = |time: Option<SystemTime>| match time {
1507 Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts),
1508 Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!(
1509 io::ErrorKind::InvalidInput,
1510 "timestamp is too large to set as a file time",
1511 )),
1512 Some(_) => Err(io::const_error!(
1513 io::ErrorKind::InvalidInput,
1514 "timestamp is too small to set as a file time",
1515 )),
1516 None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }),
1517 };
1518 cfg_if::cfg_if! {
1519 if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx"))] {
1520 let _ = times;
1524 Err(io::const_error!(
1525 io::ErrorKind::Unsupported,
1526 "setting file times not supported",
1527 ))
1528 } else if #[cfg(target_vendor = "apple")] {
1529 let mut buf = [mem::MaybeUninit::<libc::timespec>::uninit(); 3];
1530 let mut num_times = 0;
1531 let mut attrlist: libc::attrlist = unsafe { mem::zeroed() };
1532 attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT;
1533 if times.created.is_some() {
1534 buf[num_times].write(to_timespec(times.created)?);
1535 num_times += 1;
1536 attrlist.commonattr |= libc::ATTR_CMN_CRTIME;
1537 }
1538 if times.modified.is_some() {
1539 buf[num_times].write(to_timespec(times.modified)?);
1540 num_times += 1;
1541 attrlist.commonattr |= libc::ATTR_CMN_MODTIME;
1542 }
1543 if times.accessed.is_some() {
1544 buf[num_times].write(to_timespec(times.accessed)?);
1545 num_times += 1;
1546 attrlist.commonattr |= libc::ATTR_CMN_ACCTIME;
1547 }
1548 cvt(unsafe { libc::fsetattrlist(
1549 self.as_raw_fd(),
1550 (&raw const attrlist).cast::<libc::c_void>().cast_mut(),
1551 buf.as_ptr().cast::<libc::c_void>().cast_mut(),
1552 num_times * size_of::<libc::timespec>(),
1553 0
1554 ) })?;
1555 Ok(())
1556 } else if #[cfg(target_os = "android")] {
1557 let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
1558 cvt(unsafe {
1560 weak!(
1561 fn futimens(fd: c_int, times: *const libc::timespec) -> c_int;
1562 );
1563 match futimens.get() {
1564 Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()),
1565 None => return Err(io::const_error!(
1566 io::ErrorKind::Unsupported,
1567 "setting file times requires Android API level >= 19",
1568 )),
1569 }
1570 })?;
1571 Ok(())
1572 } else {
1573 #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
1574 {
1575 use crate::sys::{time::__timespec64, weak::weak};
1576
1577 weak!(
1579 fn __futimens64(fd: c_int, times: *const __timespec64) -> c_int;
1580 );
1581
1582 if let Some(futimens64) = __futimens64.get() {
1583 let to_timespec = |time: Option<SystemTime>| time.map(|time| time.t.to_timespec64())
1584 .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _));
1585 let times = [to_timespec(times.accessed), to_timespec(times.modified)];
1586 cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) })?;
1587 return Ok(());
1588 }
1589 }
1590 let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
1591 cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?;
1592 Ok(())
1593 }
1594 }
1595 }
1596}
1597
1598impl DirBuilder {
1599 pub fn new() -> DirBuilder {
1600 DirBuilder { mode: 0o777 }
1601 }
1602
1603 pub fn mkdir(&self, p: &Path) -> io::Result<()> {
1604 run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ()))
1605 }
1606
1607 pub fn set_mode(&mut self, mode: u32) {
1608 self.mode = mode as mode_t;
1609 }
1610}
1611
1612impl fmt::Debug for DirBuilder {
1613 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1614 let DirBuilder { mode } = self;
1615 f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish()
1616 }
1617}
1618
1619impl AsInner<FileDesc> for File {
1620 #[inline]
1621 fn as_inner(&self) -> &FileDesc {
1622 &self.0
1623 }
1624}
1625
1626impl AsInnerMut<FileDesc> for File {
1627 #[inline]
1628 fn as_inner_mut(&mut self) -> &mut FileDesc {
1629 &mut self.0
1630 }
1631}
1632
1633impl IntoInner<FileDesc> for File {
1634 fn into_inner(self) -> FileDesc {
1635 self.0
1636 }
1637}
1638
1639impl FromInner<FileDesc> for File {
1640 fn from_inner(file_desc: FileDesc) -> Self {
1641 Self(file_desc)
1642 }
1643}
1644
1645impl AsFd for File {
1646 #[inline]
1647 fn as_fd(&self) -> BorrowedFd<'_> {
1648 self.0.as_fd()
1649 }
1650}
1651
1652impl AsRawFd for File {
1653 #[inline]
1654 fn as_raw_fd(&self) -> RawFd {
1655 self.0.as_raw_fd()
1656 }
1657}
1658
1659impl IntoRawFd for File {
1660 fn into_raw_fd(self) -> RawFd {
1661 self.0.into_raw_fd()
1662 }
1663}
1664
1665impl FromRawFd for File {
1666 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
1667 Self(FromRawFd::from_raw_fd(raw_fd))
1668 }
1669}
1670
1671impl fmt::Debug for File {
1672 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1673 #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))]
1674 fn get_path(fd: c_int) -> Option<PathBuf> {
1675 let mut p = PathBuf::from("/proc/self/fd");
1676 p.push(&fd.to_string());
1677 run_path_with_cstr(&p, &readlink).ok()
1678 }
1679
1680 #[cfg(any(target_vendor = "apple", target_os = "netbsd"))]
1681 fn get_path(fd: c_int) -> Option<PathBuf> {
1682 let mut buf = vec![0; libc::PATH_MAX as usize];
1688 let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
1689 if n == -1 {
1690 cfg_if::cfg_if! {
1691 if #[cfg(target_os = "netbsd")] {
1692 let mut p = PathBuf::from("/proc/self/fd");
1694 p.push(&fd.to_string());
1695 return run_path_with_cstr(&p, &readlink).ok()
1696 } else {
1697 return None;
1698 }
1699 }
1700 }
1701 let l = buf.iter().position(|&c| c == 0).unwrap();
1702 buf.truncate(l as usize);
1703 buf.shrink_to_fit();
1704 Some(PathBuf::from(OsString::from_vec(buf)))
1705 }
1706
1707 #[cfg(target_os = "freebsd")]
1708 fn get_path(fd: c_int) -> Option<PathBuf> {
1709 let info = Box::<libc::kinfo_file>::new_zeroed();
1710 let mut info = unsafe { info.assume_init() };
1711 info.kf_structsize = size_of::<libc::kinfo_file>() as libc::c_int;
1712 let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) };
1713 if n == -1 {
1714 return None;
1715 }
1716 let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() };
1717 Some(PathBuf::from(OsString::from_vec(buf)))
1718 }
1719
1720 #[cfg(target_os = "vxworks")]
1721 fn get_path(fd: c_int) -> Option<PathBuf> {
1722 let mut buf = vec![0; libc::PATH_MAX as usize];
1723 let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
1724 if n == -1 {
1725 return None;
1726 }
1727 let l = buf.iter().position(|&c| c == 0).unwrap();
1728 buf.truncate(l as usize);
1729 Some(PathBuf::from(OsString::from_vec(buf)))
1730 }
1731
1732 #[cfg(not(any(
1733 target_os = "linux",
1734 target_os = "vxworks",
1735 target_os = "freebsd",
1736 target_os = "netbsd",
1737 target_os = "illumos",
1738 target_os = "solaris",
1739 target_vendor = "apple",
1740 )))]
1741 fn get_path(_fd: c_int) -> Option<PathBuf> {
1742 None
1744 }
1745
1746 fn get_mode(fd: c_int) -> Option<(bool, bool)> {
1747 let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
1748 if mode == -1 {
1749 return None;
1750 }
1751 match mode & libc::O_ACCMODE {
1752 libc::O_RDONLY => Some((true, false)),
1753 libc::O_RDWR => Some((true, true)),
1754 libc::O_WRONLY => Some((false, true)),
1755 _ => None,
1756 }
1757 }
1758
1759 let fd = self.as_raw_fd();
1760 let mut b = f.debug_struct("File");
1761 b.field("fd", &fd);
1762 if let Some(path) = get_path(fd) {
1763 b.field("path", &path);
1764 }
1765 if let Some((read, write)) = get_mode(fd) {
1766 b.field("read", &read).field("write", &write);
1767 }
1768 b.finish()
1769 }
1770}
1771
1772impl fmt::Debug for Mode {
1782 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1783 let Self(mode) = *self;
1784 write!(f, "0o{mode:06o}")?;
1785
1786 let entry_type = match mode & libc::S_IFMT {
1787 libc::S_IFDIR => 'd',
1788 libc::S_IFBLK => 'b',
1789 libc::S_IFCHR => 'c',
1790 libc::S_IFLNK => 'l',
1791 libc::S_IFIFO => 'p',
1792 libc::S_IFREG => '-',
1793 _ => return Ok(()),
1794 };
1795
1796 f.write_str(" (")?;
1797 f.write_char(entry_type)?;
1798
1799 f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?;
1801 f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?;
1802 let owner_executable = mode & libc::S_IXUSR != 0;
1803 let setuid = mode as c_int & libc::S_ISUID as c_int != 0;
1804 f.write_char(match (owner_executable, setuid) {
1805 (true, true) => 's', (false, true) => 'S', (true, false) => 'x', (false, false) => '-',
1809 })?;
1810
1811 f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?;
1813 f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?;
1814 let group_executable = mode & libc::S_IXGRP != 0;
1815 let setgid = mode as c_int & libc::S_ISGID as c_int != 0;
1816 f.write_char(match (group_executable, setgid) {
1817 (true, true) => 's', (false, true) => 'S', (true, false) => 'x', (false, false) => '-',
1821 })?;
1822
1823 f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?;
1825 f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?;
1826 let other_executable = mode & libc::S_IXOTH != 0;
1827 let sticky = mode as c_int & libc::S_ISVTX as c_int != 0;
1828 f.write_char(match (entry_type, other_executable, sticky) {
1829 ('d', true, true) => 't', ('d', false, true) => 'T', (_, true, _) => 'x', (_, false, _) => '-',
1833 })?;
1834
1835 f.write_char(')')
1836 }
1837}
1838
1839pub fn readdir(path: &Path) -> io::Result<ReadDir> {
1840 let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
1841 if ptr.is_null() {
1842 Err(Error::last_os_error())
1843 } else {
1844 let root = path.to_path_buf();
1845 let inner = InnerReadDir { dirp: Dir(ptr), root };
1846 Ok(ReadDir::new(inner))
1847 }
1848}
1849
1850pub fn unlink(p: &CStr) -> io::Result<()> {
1851 cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ())
1852}
1853
1854pub fn rename(old: &CStr, new: &CStr) -> io::Result<()> {
1855 cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ())
1856}
1857
1858pub fn set_perm(p: &CStr, perm: FilePermissions) -> io::Result<()> {
1859 cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())
1860}
1861
1862pub fn rmdir(p: &CStr) -> io::Result<()> {
1863 cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ())
1864}
1865
1866pub fn readlink(c_path: &CStr) -> io::Result<PathBuf> {
1867 let p = c_path.as_ptr();
1868
1869 let mut buf = Vec::with_capacity(256);
1870
1871 loop {
1872 let buf_read =
1873 cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
1874
1875 unsafe {
1876 buf.set_len(buf_read);
1877 }
1878
1879 if buf_read != buf.capacity() {
1880 buf.shrink_to_fit();
1881
1882 return Ok(PathBuf::from(OsString::from_vec(buf)));
1883 }
1884
1885 buf.reserve(1);
1889 }
1890}
1891
1892pub fn symlink(original: &CStr, link: &CStr) -> io::Result<()> {
1893 cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ())
1894}
1895
1896pub fn link(original: &CStr, link: &CStr) -> io::Result<()> {
1897 cfg_if::cfg_if! {
1898 if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70"))] {
1899 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1905 } else {
1906 cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
1909 }
1910 }
1911 Ok(())
1912}
1913
1914pub fn stat(p: &CStr) -> io::Result<FileAttr> {
1915 cfg_has_statx! {
1916 if let Some(ret) = unsafe { try_statx(
1917 libc::AT_FDCWD,
1918 p.as_ptr(),
1919 libc::AT_STATX_SYNC_AS_STAT,
1920 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
1921 ) } {
1922 return ret;
1923 }
1924 }
1925
1926 let mut stat: stat64 = unsafe { mem::zeroed() };
1927 cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
1928 Ok(FileAttr::from_stat64(stat))
1929}
1930
1931pub fn lstat(p: &CStr) -> io::Result<FileAttr> {
1932 cfg_has_statx! {
1933 if let Some(ret) = unsafe { try_statx(
1934 libc::AT_FDCWD,
1935 p.as_ptr(),
1936 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
1937 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
1938 ) } {
1939 return ret;
1940 }
1941 }
1942
1943 let mut stat: stat64 = unsafe { mem::zeroed() };
1944 cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
1945 Ok(FileAttr::from_stat64(stat))
1946}
1947
1948pub fn canonicalize(path: &CStr) -> io::Result<PathBuf> {
1949 let r = unsafe { libc::realpath(path.as_ptr(), ptr::null_mut()) };
1950 if r.is_null() {
1951 return Err(io::Error::last_os_error());
1952 }
1953 Ok(PathBuf::from(OsString::from_vec(unsafe {
1954 let buf = CStr::from_ptr(r).to_bytes().to_vec();
1955 libc::free(r as *mut _);
1956 buf
1957 })))
1958}
1959
1960fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1961 use crate::fs::File;
1962 use crate::sys::fs::common::NOT_FILE_ERROR;
1963
1964 let reader = File::open(from)?;
1965 let metadata = reader.metadata()?;
1966 if !metadata.is_file() {
1967 return Err(NOT_FILE_ERROR);
1968 }
1969 Ok((reader, metadata))
1970}
1971
1972#[cfg(target_os = "espidf")]
1973fn open_to_and_set_permissions(
1974 to: &Path,
1975 _reader_metadata: &crate::fs::Metadata,
1976) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1977 use crate::fs::OpenOptions;
1978 let writer = OpenOptions::new().open(to)?;
1979 let writer_metadata = writer.metadata()?;
1980 Ok((writer, writer_metadata))
1981}
1982
1983#[cfg(not(target_os = "espidf"))]
1984fn open_to_and_set_permissions(
1985 to: &Path,
1986 reader_metadata: &crate::fs::Metadata,
1987) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1988 use crate::fs::OpenOptions;
1989 use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
1990
1991 let perm = reader_metadata.permissions();
1992 let writer = OpenOptions::new()
1993 .mode(perm.mode())
1995 .write(true)
1996 .create(true)
1997 .truncate(true)
1998 .open(to)?;
1999 let writer_metadata = writer.metadata()?;
2000 #[cfg(not(target_os = "vita"))]
2002 if writer_metadata.is_file() {
2003 writer.set_permissions(perm)?;
2007 }
2008 Ok((writer, writer_metadata))
2009}
2010
2011mod cfm {
2012 use crate::fs::{File, Metadata};
2013 use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write};
2014
2015 #[allow(dead_code)]
2016 pub struct CachedFileMetadata(pub File, pub Metadata);
2017
2018 impl Read for CachedFileMetadata {
2019 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
2020 self.0.read(buf)
2021 }
2022 fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
2023 self.0.read_vectored(bufs)
2024 }
2025 fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
2026 self.0.read_buf(cursor)
2027 }
2028 #[inline]
2029 fn is_read_vectored(&self) -> bool {
2030 self.0.is_read_vectored()
2031 }
2032 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
2033 self.0.read_to_end(buf)
2034 }
2035 fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
2036 self.0.read_to_string(buf)
2037 }
2038 }
2039 impl Write for CachedFileMetadata {
2040 fn write(&mut self, buf: &[u8]) -> Result<usize> {
2041 self.0.write(buf)
2042 }
2043 fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
2044 self.0.write_vectored(bufs)
2045 }
2046 #[inline]
2047 fn is_write_vectored(&self) -> bool {
2048 self.0.is_write_vectored()
2049 }
2050 #[inline]
2051 fn flush(&mut self) -> Result<()> {
2052 self.0.flush()
2053 }
2054 }
2055}
2056#[cfg(any(target_os = "linux", target_os = "android"))]
2057pub(crate) use cfm::CachedFileMetadata;
2058
2059#[cfg(not(target_vendor = "apple"))]
2060pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
2061 let (reader, reader_metadata) = open_from(from)?;
2062 let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
2063
2064 io::copy(
2065 &mut cfm::CachedFileMetadata(reader, reader_metadata),
2066 &mut cfm::CachedFileMetadata(writer, writer_metadata),
2067 )
2068}
2069
2070#[cfg(target_vendor = "apple")]
2071pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
2072 const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA;
2073
2074 struct FreeOnDrop(libc::copyfile_state_t);
2075 impl Drop for FreeOnDrop {
2076 fn drop(&mut self) {
2077 unsafe {
2079 libc::copyfile_state_free(self.0);
2082 }
2083 }
2084 }
2085
2086 let (reader, reader_metadata) = open_from(from)?;
2087
2088 let clonefile_result = run_path_with_cstr(to, &|to| {
2089 cvt(unsafe { libc::fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) })
2090 });
2091 match clonefile_result {
2092 Ok(_) => return Ok(reader_metadata.len()),
2093 Err(e) => match e.raw_os_error() {
2094 Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
2099 _ => return Err(e),
2100 },
2101 }
2102
2103 let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
2105
2106 let state = unsafe {
2109 let state = libc::copyfile_state_alloc();
2110 if state.is_null() {
2111 return Err(crate::io::Error::last_os_error());
2112 }
2113 FreeOnDrop(state)
2114 };
2115
2116 let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA };
2117
2118 cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
2119
2120 let mut bytes_copied: libc::off_t = 0;
2121 cvt(unsafe {
2122 libc::copyfile_state_get(
2123 state.0,
2124 libc::COPYFILE_STATE_COPIED as u32,
2125 (&raw mut bytes_copied) as *mut libc::c_void,
2126 )
2127 })?;
2128 Ok(bytes_copied as u64)
2129}
2130
2131pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
2132 run_path_with_cstr(path, &|path| {
2133 cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
2134 .map(|_| ())
2135 })
2136}
2137
2138pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
2139 cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?;
2140 Ok(())
2141}
2142
2143#[cfg(not(target_os = "vxworks"))]
2144pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
2145 run_path_with_cstr(path, &|path| {
2146 cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
2147 .map(|_| ())
2148 })
2149}
2150
2151#[cfg(target_os = "vxworks")]
2152pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
2153 let (_, _, _) = (path, uid, gid);
2154 Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks"))
2155}
2156
2157#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
2158pub fn chroot(dir: &Path) -> io::Result<()> {
2159 run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ()))
2160}
2161
2162#[cfg(target_os = "vxworks")]
2163pub fn chroot(dir: &Path) -> io::Result<()> {
2164 let _ = dir;
2165 Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks"))
2166}
2167
2168pub fn mkfifo(path: &Path, mode: u32) -> io::Result<()> {
2169 run_path_with_cstr(path, &|path| {
2170 cvt(unsafe { libc::mkfifo(path.as_ptr(), mode.try_into().unwrap()) }).map(|_| ())
2171 })
2172}
2173
2174pub use remove_dir_impl::remove_dir_all;
2175
2176#[cfg(any(
2178 target_os = "redox",
2179 target_os = "espidf",
2180 target_os = "horizon",
2181 target_os = "vita",
2182 target_os = "nto",
2183 target_os = "vxworks",
2184 miri
2185))]
2186mod remove_dir_impl {
2187 pub use crate::sys::fs::common::remove_dir_all;
2188}
2189
2190#[cfg(not(any(
2192 target_os = "redox",
2193 target_os = "espidf",
2194 target_os = "horizon",
2195 target_os = "vita",
2196 target_os = "nto",
2197 target_os = "vxworks",
2198 miri
2199)))]
2200mod remove_dir_impl {
2201 #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
2202 use libc::{fdopendir, openat, unlinkat};
2203 #[cfg(all(target_os = "linux", target_env = "gnu"))]
2204 use libc::{fdopendir, openat64 as openat, unlinkat};
2205
2206 use super::{Dir, DirEntry, InnerReadDir, ReadDir, lstat};
2207 use crate::ffi::CStr;
2208 use crate::io;
2209 use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
2210 use crate::os::unix::prelude::{OwnedFd, RawFd};
2211 use crate::path::{Path, PathBuf};
2212 use crate::sys::common::small_c_string::run_path_with_cstr;
2213 use crate::sys::{cvt, cvt_r};
2214 use crate::sys_common::ignore_notfound;
2215
2216 pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
2217 let fd = cvt_r(|| unsafe {
2218 openat(
2219 parent_fd.unwrap_or(libc::AT_FDCWD),
2220 p.as_ptr(),
2221 libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
2222 )
2223 })?;
2224 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
2225 }
2226
2227 fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
2228 let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) };
2229 if ptr.is_null() {
2230 return Err(io::Error::last_os_error());
2231 }
2232 let dirp = Dir(ptr);
2233 let new_parent_fd = dir_fd.into_raw_fd();
2235 let dummy_root = PathBuf::new();
2238 let inner = InnerReadDir { dirp, root: dummy_root };
2239 Ok((ReadDir::new(inner), new_parent_fd))
2240 }
2241
2242 #[cfg(any(
2243 target_os = "solaris",
2244 target_os = "illumos",
2245 target_os = "haiku",
2246 target_os = "vxworks",
2247 target_os = "aix",
2248 ))]
2249 fn is_dir(_ent: &DirEntry) -> Option<bool> {
2250 None
2251 }
2252
2253 #[cfg(not(any(
2254 target_os = "solaris",
2255 target_os = "illumos",
2256 target_os = "haiku",
2257 target_os = "vxworks",
2258 target_os = "aix",
2259 )))]
2260 fn is_dir(ent: &DirEntry) -> Option<bool> {
2261 match ent.entry.d_type {
2262 libc::DT_UNKNOWN => None,
2263 libc::DT_DIR => Some(true),
2264 _ => Some(false),
2265 }
2266 }
2267
2268 fn is_enoent(result: &io::Result<()>) -> bool {
2269 if let Err(err) = result
2270 && matches!(err.raw_os_error(), Some(libc::ENOENT))
2271 {
2272 true
2273 } else {
2274 false
2275 }
2276 }
2277
2278 fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {
2279 let fd = match openat_nofollow_dironly(parent_fd, &path) {
2281 Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => {
2282 return match parent_fd {
2285 Some(parent_fd) => {
2287 cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }).map(drop)
2288 }
2289 None => Err(err),
2291 };
2292 }
2293 result => result?,
2294 };
2295
2296 let (dir, fd) = fdreaddir(fd)?;
2298 for child in dir {
2299 let child = child?;
2300 let child_name = child.name_cstr();
2301 let result: io::Result<()> = try {
2305 match is_dir(&child) {
2306 Some(true) => {
2307 remove_dir_all_recursive(Some(fd), child_name)?;
2308 }
2309 Some(false) => {
2310 cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?;
2311 }
2312 None => {
2313 remove_dir_all_recursive(Some(fd), child_name)?;
2318 }
2319 }
2320 };
2321 if result.is_err() && !is_enoent(&result) {
2322 return result;
2323 }
2324 }
2325
2326 ignore_notfound(cvt(unsafe {
2328 unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR)
2329 }))?;
2330 Ok(())
2331 }
2332
2333 fn remove_dir_all_modern(p: &CStr) -> io::Result<()> {
2334 let attr = lstat(p)?;
2338 if attr.file_type().is_symlink() {
2339 super::unlink(p)
2340 } else {
2341 remove_dir_all_recursive(None, &p)
2342 }
2343 }
2344
2345 pub fn remove_dir_all(p: &Path) -> io::Result<()> {
2346 run_path_with_cstr(p, &remove_dir_all_modern)
2347 }
2348}