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_vendor = "apple",
1267 ))]
1268 pub fn lock(&self) -> io::Result<()> {
1269 cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?;
1270 return Ok(());
1271 }
1272
1273 #[cfg(not(any(
1274 target_os = "freebsd",
1275 target_os = "fuchsia",
1276 target_os = "linux",
1277 target_os = "netbsd",
1278 target_vendor = "apple",
1279 )))]
1280 pub fn lock(&self) -> io::Result<()> {
1281 Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported"))
1282 }
1283
1284 #[cfg(any(
1285 target_os = "freebsd",
1286 target_os = "fuchsia",
1287 target_os = "linux",
1288 target_os = "netbsd",
1289 target_vendor = "apple",
1290 ))]
1291 pub fn lock_shared(&self) -> io::Result<()> {
1292 cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?;
1293 return Ok(());
1294 }
1295
1296 #[cfg(not(any(
1297 target_os = "freebsd",
1298 target_os = "fuchsia",
1299 target_os = "linux",
1300 target_os = "netbsd",
1301 target_vendor = "apple",
1302 )))]
1303 pub fn lock_shared(&self) -> io::Result<()> {
1304 Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported"))
1305 }
1306
1307 #[cfg(any(
1308 target_os = "freebsd",
1309 target_os = "fuchsia",
1310 target_os = "linux",
1311 target_os = "netbsd",
1312 target_vendor = "apple",
1313 ))]
1314 pub fn try_lock(&self) -> Result<(), TryLockError> {
1315 let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) });
1316 if let Err(err) = result {
1317 if err.kind() == io::ErrorKind::WouldBlock {
1318 Err(TryLockError::WouldBlock)
1319 } else {
1320 Err(TryLockError::Error(err))
1321 }
1322 } else {
1323 Ok(())
1324 }
1325 }
1326
1327 #[cfg(not(any(
1328 target_os = "freebsd",
1329 target_os = "fuchsia",
1330 target_os = "linux",
1331 target_os = "netbsd",
1332 target_vendor = "apple",
1333 )))]
1334 pub fn try_lock(&self) -> Result<(), TryLockError> {
1335 Err(TryLockError::Error(io::const_error!(
1336 io::ErrorKind::Unsupported,
1337 "try_lock() not supported"
1338 )))
1339 }
1340
1341 #[cfg(any(
1342 target_os = "freebsd",
1343 target_os = "fuchsia",
1344 target_os = "linux",
1345 target_os = "netbsd",
1346 target_vendor = "apple",
1347 ))]
1348 pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
1349 let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) });
1350 if let Err(err) = result {
1351 if err.kind() == io::ErrorKind::WouldBlock {
1352 Err(TryLockError::WouldBlock)
1353 } else {
1354 Err(TryLockError::Error(err))
1355 }
1356 } else {
1357 Ok(())
1358 }
1359 }
1360
1361 #[cfg(not(any(
1362 target_os = "freebsd",
1363 target_os = "fuchsia",
1364 target_os = "linux",
1365 target_os = "netbsd",
1366 target_vendor = "apple",
1367 )))]
1368 pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
1369 Err(TryLockError::Error(io::const_error!(
1370 io::ErrorKind::Unsupported,
1371 "try_lock_shared() not supported"
1372 )))
1373 }
1374
1375 #[cfg(any(
1376 target_os = "freebsd",
1377 target_os = "fuchsia",
1378 target_os = "linux",
1379 target_os = "netbsd",
1380 target_vendor = "apple",
1381 ))]
1382 pub fn unlock(&self) -> io::Result<()> {
1383 cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?;
1384 return Ok(());
1385 }
1386
1387 #[cfg(not(any(
1388 target_os = "freebsd",
1389 target_os = "fuchsia",
1390 target_os = "linux",
1391 target_os = "netbsd",
1392 target_vendor = "apple",
1393 )))]
1394 pub fn unlock(&self) -> io::Result<()> {
1395 Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported"))
1396 }
1397
1398 pub fn truncate(&self, size: u64) -> io::Result<()> {
1399 let size: off64_t =
1400 size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1401 cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
1402 }
1403
1404 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
1405 self.0.read(buf)
1406 }
1407
1408 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
1409 self.0.read_vectored(bufs)
1410 }
1411
1412 #[inline]
1413 pub fn is_read_vectored(&self) -> bool {
1414 self.0.is_read_vectored()
1415 }
1416
1417 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
1418 self.0.read_at(buf, offset)
1419 }
1420
1421 pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
1422 self.0.read_buf(cursor)
1423 }
1424
1425 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
1426 self.0.read_vectored_at(bufs, offset)
1427 }
1428
1429 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
1430 self.0.write(buf)
1431 }
1432
1433 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
1434 self.0.write_vectored(bufs)
1435 }
1436
1437 #[inline]
1438 pub fn is_write_vectored(&self) -> bool {
1439 self.0.is_write_vectored()
1440 }
1441
1442 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
1443 self.0.write_at(buf, offset)
1444 }
1445
1446 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
1447 self.0.write_vectored_at(bufs, offset)
1448 }
1449
1450 #[inline]
1451 pub fn flush(&self) -> io::Result<()> {
1452 Ok(())
1453 }
1454
1455 pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
1456 let (whence, pos) = match pos {
1457 SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
1460 SeekFrom::End(off) => (libc::SEEK_END, off),
1461 SeekFrom::Current(off) => (libc::SEEK_CUR, off),
1462 };
1463 let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) })?;
1464 Ok(n as u64)
1465 }
1466
1467 pub fn size(&self) -> Option<io::Result<u64>> {
1468 match self.file_attr().map(|attr| attr.size()) {
1469 Ok(0) => None,
1472 result => Some(result),
1473 }
1474 }
1475
1476 pub fn tell(&self) -> io::Result<u64> {
1477 self.seek(SeekFrom::Current(0))
1478 }
1479
1480 pub fn duplicate(&self) -> io::Result<File> {
1481 self.0.duplicate().map(File)
1482 }
1483
1484 pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
1485 cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
1486 Ok(())
1487 }
1488
1489 pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
1490 #[cfg(not(any(
1491 target_os = "redox",
1492 target_os = "espidf",
1493 target_os = "horizon",
1494 target_os = "nuttx",
1495 )))]
1496 let to_timespec = |time: Option<SystemTime>| match time {
1497 Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts),
1498 Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!(
1499 io::ErrorKind::InvalidInput,
1500 "timestamp is too large to set as a file time",
1501 )),
1502 Some(_) => Err(io::const_error!(
1503 io::ErrorKind::InvalidInput,
1504 "timestamp is too small to set as a file time",
1505 )),
1506 None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }),
1507 };
1508 cfg_if::cfg_if! {
1509 if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx"))] {
1510 let _ = times;
1514 Err(io::const_error!(
1515 io::ErrorKind::Unsupported,
1516 "setting file times not supported",
1517 ))
1518 } else if #[cfg(target_vendor = "apple")] {
1519 let mut buf = [mem::MaybeUninit::<libc::timespec>::uninit(); 3];
1520 let mut num_times = 0;
1521 let mut attrlist: libc::attrlist = unsafe { mem::zeroed() };
1522 attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT;
1523 if times.created.is_some() {
1524 buf[num_times].write(to_timespec(times.created)?);
1525 num_times += 1;
1526 attrlist.commonattr |= libc::ATTR_CMN_CRTIME;
1527 }
1528 if times.modified.is_some() {
1529 buf[num_times].write(to_timespec(times.modified)?);
1530 num_times += 1;
1531 attrlist.commonattr |= libc::ATTR_CMN_MODTIME;
1532 }
1533 if times.accessed.is_some() {
1534 buf[num_times].write(to_timespec(times.accessed)?);
1535 num_times += 1;
1536 attrlist.commonattr |= libc::ATTR_CMN_ACCTIME;
1537 }
1538 cvt(unsafe { libc::fsetattrlist(
1539 self.as_raw_fd(),
1540 (&raw const attrlist).cast::<libc::c_void>().cast_mut(),
1541 buf.as_ptr().cast::<libc::c_void>().cast_mut(),
1542 num_times * size_of::<libc::timespec>(),
1543 0
1544 ) })?;
1545 Ok(())
1546 } else if #[cfg(target_os = "android")] {
1547 let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
1548 cvt(unsafe {
1550 weak!(
1551 fn futimens(fd: c_int, times: *const libc::timespec) -> c_int;
1552 );
1553 match futimens.get() {
1554 Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()),
1555 None => return Err(io::const_error!(
1556 io::ErrorKind::Unsupported,
1557 "setting file times requires Android API level >= 19",
1558 )),
1559 }
1560 })?;
1561 Ok(())
1562 } else {
1563 #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
1564 {
1565 use crate::sys::{time::__timespec64, weak::weak};
1566
1567 weak!(
1569 fn __futimens64(fd: c_int, times: *const __timespec64) -> c_int;
1570 );
1571
1572 if let Some(futimens64) = __futimens64.get() {
1573 let to_timespec = |time: Option<SystemTime>| time.map(|time| time.t.to_timespec64())
1574 .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _));
1575 let times = [to_timespec(times.accessed), to_timespec(times.modified)];
1576 cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) })?;
1577 return Ok(());
1578 }
1579 }
1580 let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
1581 cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?;
1582 Ok(())
1583 }
1584 }
1585 }
1586}
1587
1588impl DirBuilder {
1589 pub fn new() -> DirBuilder {
1590 DirBuilder { mode: 0o777 }
1591 }
1592
1593 pub fn mkdir(&self, p: &Path) -> io::Result<()> {
1594 run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ()))
1595 }
1596
1597 pub fn set_mode(&mut self, mode: u32) {
1598 self.mode = mode as mode_t;
1599 }
1600}
1601
1602impl fmt::Debug for DirBuilder {
1603 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1604 let DirBuilder { mode } = self;
1605 f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish()
1606 }
1607}
1608
1609impl AsInner<FileDesc> for File {
1610 #[inline]
1611 fn as_inner(&self) -> &FileDesc {
1612 &self.0
1613 }
1614}
1615
1616impl AsInnerMut<FileDesc> for File {
1617 #[inline]
1618 fn as_inner_mut(&mut self) -> &mut FileDesc {
1619 &mut self.0
1620 }
1621}
1622
1623impl IntoInner<FileDesc> for File {
1624 fn into_inner(self) -> FileDesc {
1625 self.0
1626 }
1627}
1628
1629impl FromInner<FileDesc> for File {
1630 fn from_inner(file_desc: FileDesc) -> Self {
1631 Self(file_desc)
1632 }
1633}
1634
1635impl AsFd for File {
1636 #[inline]
1637 fn as_fd(&self) -> BorrowedFd<'_> {
1638 self.0.as_fd()
1639 }
1640}
1641
1642impl AsRawFd for File {
1643 #[inline]
1644 fn as_raw_fd(&self) -> RawFd {
1645 self.0.as_raw_fd()
1646 }
1647}
1648
1649impl IntoRawFd for File {
1650 fn into_raw_fd(self) -> RawFd {
1651 self.0.into_raw_fd()
1652 }
1653}
1654
1655impl FromRawFd for File {
1656 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
1657 Self(FromRawFd::from_raw_fd(raw_fd))
1658 }
1659}
1660
1661impl fmt::Debug for File {
1662 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1663 #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))]
1664 fn get_path(fd: c_int) -> Option<PathBuf> {
1665 let mut p = PathBuf::from("/proc/self/fd");
1666 p.push(&fd.to_string());
1667 run_path_with_cstr(&p, &readlink).ok()
1668 }
1669
1670 #[cfg(any(target_vendor = "apple", target_os = "netbsd"))]
1671 fn get_path(fd: c_int) -> Option<PathBuf> {
1672 let mut buf = vec![0; libc::PATH_MAX as usize];
1678 let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
1679 if n == -1 {
1680 cfg_if::cfg_if! {
1681 if #[cfg(target_os = "netbsd")] {
1682 let mut p = PathBuf::from("/proc/self/fd");
1684 p.push(&fd.to_string());
1685 return run_path_with_cstr(&p, &readlink).ok()
1686 } else {
1687 return None;
1688 }
1689 }
1690 }
1691 let l = buf.iter().position(|&c| c == 0).unwrap();
1692 buf.truncate(l as usize);
1693 buf.shrink_to_fit();
1694 Some(PathBuf::from(OsString::from_vec(buf)))
1695 }
1696
1697 #[cfg(target_os = "freebsd")]
1698 fn get_path(fd: c_int) -> Option<PathBuf> {
1699 let info = Box::<libc::kinfo_file>::new_zeroed();
1700 let mut info = unsafe { info.assume_init() };
1701 info.kf_structsize = size_of::<libc::kinfo_file>() as libc::c_int;
1702 let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) };
1703 if n == -1 {
1704 return None;
1705 }
1706 let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() };
1707 Some(PathBuf::from(OsString::from_vec(buf)))
1708 }
1709
1710 #[cfg(target_os = "vxworks")]
1711 fn get_path(fd: c_int) -> Option<PathBuf> {
1712 let mut buf = vec![0; libc::PATH_MAX as usize];
1713 let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
1714 if n == -1 {
1715 return None;
1716 }
1717 let l = buf.iter().position(|&c| c == 0).unwrap();
1718 buf.truncate(l as usize);
1719 Some(PathBuf::from(OsString::from_vec(buf)))
1720 }
1721
1722 #[cfg(not(any(
1723 target_os = "linux",
1724 target_os = "vxworks",
1725 target_os = "freebsd",
1726 target_os = "netbsd",
1727 target_os = "illumos",
1728 target_os = "solaris",
1729 target_vendor = "apple",
1730 )))]
1731 fn get_path(_fd: c_int) -> Option<PathBuf> {
1732 None
1734 }
1735
1736 fn get_mode(fd: c_int) -> Option<(bool, bool)> {
1737 let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
1738 if mode == -1 {
1739 return None;
1740 }
1741 match mode & libc::O_ACCMODE {
1742 libc::O_RDONLY => Some((true, false)),
1743 libc::O_RDWR => Some((true, true)),
1744 libc::O_WRONLY => Some((false, true)),
1745 _ => None,
1746 }
1747 }
1748
1749 let fd = self.as_raw_fd();
1750 let mut b = f.debug_struct("File");
1751 b.field("fd", &fd);
1752 if let Some(path) = get_path(fd) {
1753 b.field("path", &path);
1754 }
1755 if let Some((read, write)) = get_mode(fd) {
1756 b.field("read", &read).field("write", &write);
1757 }
1758 b.finish()
1759 }
1760}
1761
1762impl fmt::Debug for Mode {
1772 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1773 let Self(mode) = *self;
1774 write!(f, "0o{mode:06o}")?;
1775
1776 let entry_type = match mode & libc::S_IFMT {
1777 libc::S_IFDIR => 'd',
1778 libc::S_IFBLK => 'b',
1779 libc::S_IFCHR => 'c',
1780 libc::S_IFLNK => 'l',
1781 libc::S_IFIFO => 'p',
1782 libc::S_IFREG => '-',
1783 _ => return Ok(()),
1784 };
1785
1786 f.write_str(" (")?;
1787 f.write_char(entry_type)?;
1788
1789 f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?;
1791 f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?;
1792 let owner_executable = mode & libc::S_IXUSR != 0;
1793 let setuid = mode as c_int & libc::S_ISUID as c_int != 0;
1794 f.write_char(match (owner_executable, setuid) {
1795 (true, true) => 's', (false, true) => 'S', (true, false) => 'x', (false, false) => '-',
1799 })?;
1800
1801 f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?;
1803 f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?;
1804 let group_executable = mode & libc::S_IXGRP != 0;
1805 let setgid = mode as c_int & libc::S_ISGID as c_int != 0;
1806 f.write_char(match (group_executable, setgid) {
1807 (true, true) => 's', (false, true) => 'S', (true, false) => 'x', (false, false) => '-',
1811 })?;
1812
1813 f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?;
1815 f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?;
1816 let other_executable = mode & libc::S_IXOTH != 0;
1817 let sticky = mode as c_int & libc::S_ISVTX as c_int != 0;
1818 f.write_char(match (entry_type, other_executable, sticky) {
1819 ('d', true, true) => 't', ('d', false, true) => 'T', (_, true, _) => 'x', (_, false, _) => '-',
1823 })?;
1824
1825 f.write_char(')')
1826 }
1827}
1828
1829pub fn readdir(path: &Path) -> io::Result<ReadDir> {
1830 let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
1831 if ptr.is_null() {
1832 Err(Error::last_os_error())
1833 } else {
1834 let root = path.to_path_buf();
1835 let inner = InnerReadDir { dirp: Dir(ptr), root };
1836 Ok(ReadDir::new(inner))
1837 }
1838}
1839
1840pub fn unlink(p: &CStr) -> io::Result<()> {
1841 cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ())
1842}
1843
1844pub fn rename(old: &CStr, new: &CStr) -> io::Result<()> {
1845 cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ())
1846}
1847
1848pub fn set_perm(p: &CStr, perm: FilePermissions) -> io::Result<()> {
1849 cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())
1850}
1851
1852pub fn rmdir(p: &CStr) -> io::Result<()> {
1853 cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ())
1854}
1855
1856pub fn readlink(c_path: &CStr) -> io::Result<PathBuf> {
1857 let p = c_path.as_ptr();
1858
1859 let mut buf = Vec::with_capacity(256);
1860
1861 loop {
1862 let buf_read =
1863 cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
1864
1865 unsafe {
1866 buf.set_len(buf_read);
1867 }
1868
1869 if buf_read != buf.capacity() {
1870 buf.shrink_to_fit();
1871
1872 return Ok(PathBuf::from(OsString::from_vec(buf)));
1873 }
1874
1875 buf.reserve(1);
1879 }
1880}
1881
1882pub fn symlink(original: &CStr, link: &CStr) -> io::Result<()> {
1883 cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ())
1884}
1885
1886pub fn link(original: &CStr, link: &CStr) -> io::Result<()> {
1887 cfg_if::cfg_if! {
1888 if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70"))] {
1889 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1895 } else {
1896 cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
1899 }
1900 }
1901 Ok(())
1902}
1903
1904pub fn stat(p: &CStr) -> io::Result<FileAttr> {
1905 cfg_has_statx! {
1906 if let Some(ret) = unsafe { try_statx(
1907 libc::AT_FDCWD,
1908 p.as_ptr(),
1909 libc::AT_STATX_SYNC_AS_STAT,
1910 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
1911 ) } {
1912 return ret;
1913 }
1914 }
1915
1916 let mut stat: stat64 = unsafe { mem::zeroed() };
1917 cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
1918 Ok(FileAttr::from_stat64(stat))
1919}
1920
1921pub fn lstat(p: &CStr) -> io::Result<FileAttr> {
1922 cfg_has_statx! {
1923 if let Some(ret) = unsafe { try_statx(
1924 libc::AT_FDCWD,
1925 p.as_ptr(),
1926 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
1927 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
1928 ) } {
1929 return ret;
1930 }
1931 }
1932
1933 let mut stat: stat64 = unsafe { mem::zeroed() };
1934 cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
1935 Ok(FileAttr::from_stat64(stat))
1936}
1937
1938pub fn canonicalize(path: &CStr) -> io::Result<PathBuf> {
1939 let r = unsafe { libc::realpath(path.as_ptr(), ptr::null_mut()) };
1940 if r.is_null() {
1941 return Err(io::Error::last_os_error());
1942 }
1943 Ok(PathBuf::from(OsString::from_vec(unsafe {
1944 let buf = CStr::from_ptr(r).to_bytes().to_vec();
1945 libc::free(r as *mut _);
1946 buf
1947 })))
1948}
1949
1950fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1951 use crate::fs::File;
1952 use crate::sys::fs::common::NOT_FILE_ERROR;
1953
1954 let reader = File::open(from)?;
1955 let metadata = reader.metadata()?;
1956 if !metadata.is_file() {
1957 return Err(NOT_FILE_ERROR);
1958 }
1959 Ok((reader, metadata))
1960}
1961
1962#[cfg(target_os = "espidf")]
1963fn open_to_and_set_permissions(
1964 to: &Path,
1965 _reader_metadata: &crate::fs::Metadata,
1966) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1967 use crate::fs::OpenOptions;
1968 let writer = OpenOptions::new().open(to)?;
1969 let writer_metadata = writer.metadata()?;
1970 Ok((writer, writer_metadata))
1971}
1972
1973#[cfg(not(target_os = "espidf"))]
1974fn open_to_and_set_permissions(
1975 to: &Path,
1976 reader_metadata: &crate::fs::Metadata,
1977) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1978 use crate::fs::OpenOptions;
1979 use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
1980
1981 let perm = reader_metadata.permissions();
1982 let writer = OpenOptions::new()
1983 .mode(perm.mode())
1985 .write(true)
1986 .create(true)
1987 .truncate(true)
1988 .open(to)?;
1989 let writer_metadata = writer.metadata()?;
1990 #[cfg(not(target_os = "vita"))]
1992 if writer_metadata.is_file() {
1993 writer.set_permissions(perm)?;
1997 }
1998 Ok((writer, writer_metadata))
1999}
2000
2001mod cfm {
2002 use crate::fs::{File, Metadata};
2003 use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write};
2004
2005 #[allow(dead_code)]
2006 pub struct CachedFileMetadata(pub File, pub Metadata);
2007
2008 impl Read for CachedFileMetadata {
2009 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
2010 self.0.read(buf)
2011 }
2012 fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
2013 self.0.read_vectored(bufs)
2014 }
2015 fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
2016 self.0.read_buf(cursor)
2017 }
2018 #[inline]
2019 fn is_read_vectored(&self) -> bool {
2020 self.0.is_read_vectored()
2021 }
2022 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
2023 self.0.read_to_end(buf)
2024 }
2025 fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
2026 self.0.read_to_string(buf)
2027 }
2028 }
2029 impl Write for CachedFileMetadata {
2030 fn write(&mut self, buf: &[u8]) -> Result<usize> {
2031 self.0.write(buf)
2032 }
2033 fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
2034 self.0.write_vectored(bufs)
2035 }
2036 #[inline]
2037 fn is_write_vectored(&self) -> bool {
2038 self.0.is_write_vectored()
2039 }
2040 #[inline]
2041 fn flush(&mut self) -> Result<()> {
2042 self.0.flush()
2043 }
2044 }
2045}
2046#[cfg(any(target_os = "linux", target_os = "android"))]
2047pub(crate) use cfm::CachedFileMetadata;
2048
2049#[cfg(not(target_vendor = "apple"))]
2050pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
2051 let (reader, reader_metadata) = open_from(from)?;
2052 let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
2053
2054 io::copy(
2055 &mut cfm::CachedFileMetadata(reader, reader_metadata),
2056 &mut cfm::CachedFileMetadata(writer, writer_metadata),
2057 )
2058}
2059
2060#[cfg(target_vendor = "apple")]
2061pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
2062 const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA;
2063
2064 struct FreeOnDrop(libc::copyfile_state_t);
2065 impl Drop for FreeOnDrop {
2066 fn drop(&mut self) {
2067 unsafe {
2069 libc::copyfile_state_free(self.0);
2072 }
2073 }
2074 }
2075
2076 let (reader, reader_metadata) = open_from(from)?;
2077
2078 let clonefile_result = run_path_with_cstr(to, &|to| {
2079 cvt(unsafe { libc::fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) })
2080 });
2081 match clonefile_result {
2082 Ok(_) => return Ok(reader_metadata.len()),
2083 Err(e) => match e.raw_os_error() {
2084 Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
2089 _ => return Err(e),
2090 },
2091 }
2092
2093 let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
2095
2096 let state = unsafe {
2099 let state = libc::copyfile_state_alloc();
2100 if state.is_null() {
2101 return Err(crate::io::Error::last_os_error());
2102 }
2103 FreeOnDrop(state)
2104 };
2105
2106 let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA };
2107
2108 cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
2109
2110 let mut bytes_copied: libc::off_t = 0;
2111 cvt(unsafe {
2112 libc::copyfile_state_get(
2113 state.0,
2114 libc::COPYFILE_STATE_COPIED as u32,
2115 (&raw mut bytes_copied) as *mut libc::c_void,
2116 )
2117 })?;
2118 Ok(bytes_copied as u64)
2119}
2120
2121pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
2122 run_path_with_cstr(path, &|path| {
2123 cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
2124 .map(|_| ())
2125 })
2126}
2127
2128pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
2129 cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?;
2130 Ok(())
2131}
2132
2133#[cfg(not(target_os = "vxworks"))]
2134pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
2135 run_path_with_cstr(path, &|path| {
2136 cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
2137 .map(|_| ())
2138 })
2139}
2140
2141#[cfg(target_os = "vxworks")]
2142pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
2143 let (_, _, _) = (path, uid, gid);
2144 Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks"))
2145}
2146
2147#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
2148pub fn chroot(dir: &Path) -> io::Result<()> {
2149 run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ()))
2150}
2151
2152#[cfg(target_os = "vxworks")]
2153pub fn chroot(dir: &Path) -> io::Result<()> {
2154 let _ = dir;
2155 Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks"))
2156}
2157
2158pub fn mkfifo(path: &Path, mode: u32) -> io::Result<()> {
2159 run_path_with_cstr(path, &|path| {
2160 cvt(unsafe { libc::mkfifo(path.as_ptr(), mode.try_into().unwrap()) }).map(|_| ())
2161 })
2162}
2163
2164pub use remove_dir_impl::remove_dir_all;
2165
2166#[cfg(any(
2168 target_os = "redox",
2169 target_os = "espidf",
2170 target_os = "horizon",
2171 target_os = "vita",
2172 target_os = "nto",
2173 target_os = "vxworks",
2174 miri
2175))]
2176mod remove_dir_impl {
2177 pub use crate::sys::fs::common::remove_dir_all;
2178}
2179
2180#[cfg(not(any(
2182 target_os = "redox",
2183 target_os = "espidf",
2184 target_os = "horizon",
2185 target_os = "vita",
2186 target_os = "nto",
2187 target_os = "vxworks",
2188 miri
2189)))]
2190mod remove_dir_impl {
2191 #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
2192 use libc::{fdopendir, openat, unlinkat};
2193 #[cfg(all(target_os = "linux", target_env = "gnu"))]
2194 use libc::{fdopendir, openat64 as openat, unlinkat};
2195
2196 use super::{Dir, DirEntry, InnerReadDir, ReadDir, lstat};
2197 use crate::ffi::CStr;
2198 use crate::io;
2199 use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
2200 use crate::os::unix::prelude::{OwnedFd, RawFd};
2201 use crate::path::{Path, PathBuf};
2202 use crate::sys::common::small_c_string::run_path_with_cstr;
2203 use crate::sys::{cvt, cvt_r};
2204 use crate::sys_common::ignore_notfound;
2205
2206 pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
2207 let fd = cvt_r(|| unsafe {
2208 openat(
2209 parent_fd.unwrap_or(libc::AT_FDCWD),
2210 p.as_ptr(),
2211 libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
2212 )
2213 })?;
2214 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
2215 }
2216
2217 fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
2218 let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) };
2219 if ptr.is_null() {
2220 return Err(io::Error::last_os_error());
2221 }
2222 let dirp = Dir(ptr);
2223 let new_parent_fd = dir_fd.into_raw_fd();
2225 let dummy_root = PathBuf::new();
2228 let inner = InnerReadDir { dirp, root: dummy_root };
2229 Ok((ReadDir::new(inner), new_parent_fd))
2230 }
2231
2232 #[cfg(any(
2233 target_os = "solaris",
2234 target_os = "illumos",
2235 target_os = "haiku",
2236 target_os = "vxworks",
2237 target_os = "aix",
2238 ))]
2239 fn is_dir(_ent: &DirEntry) -> Option<bool> {
2240 None
2241 }
2242
2243 #[cfg(not(any(
2244 target_os = "solaris",
2245 target_os = "illumos",
2246 target_os = "haiku",
2247 target_os = "vxworks",
2248 target_os = "aix",
2249 )))]
2250 fn is_dir(ent: &DirEntry) -> Option<bool> {
2251 match ent.entry.d_type {
2252 libc::DT_UNKNOWN => None,
2253 libc::DT_DIR => Some(true),
2254 _ => Some(false),
2255 }
2256 }
2257
2258 fn is_enoent(result: &io::Result<()>) -> bool {
2259 if let Err(err) = result
2260 && matches!(err.raw_os_error(), Some(libc::ENOENT))
2261 {
2262 true
2263 } else {
2264 false
2265 }
2266 }
2267
2268 fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {
2269 let fd = match openat_nofollow_dironly(parent_fd, &path) {
2271 Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => {
2272 return match parent_fd {
2275 Some(parent_fd) => {
2277 cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }).map(drop)
2278 }
2279 None => Err(err),
2281 };
2282 }
2283 result => result?,
2284 };
2285
2286 let (dir, fd) = fdreaddir(fd)?;
2288 for child in dir {
2289 let child = child?;
2290 let child_name = child.name_cstr();
2291 let result: io::Result<()> = try {
2295 match is_dir(&child) {
2296 Some(true) => {
2297 remove_dir_all_recursive(Some(fd), child_name)?;
2298 }
2299 Some(false) => {
2300 cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?;
2301 }
2302 None => {
2303 remove_dir_all_recursive(Some(fd), child_name)?;
2308 }
2309 }
2310 };
2311 if result.is_err() && !is_enoent(&result) {
2312 return result;
2313 }
2314 }
2315
2316 ignore_notfound(cvt(unsafe {
2318 unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR)
2319 }))?;
2320 Ok(())
2321 }
2322
2323 fn remove_dir_all_modern(p: &CStr) -> io::Result<()> {
2324 let attr = lstat(p)?;
2328 if attr.file_type().is_symlink() {
2329 super::unlink(p)
2330 } else {
2331 remove_dir_all_recursive(None, &p)
2332 }
2333 }
2334
2335 pub fn remove_dir_all(p: &Path) -> io::Result<()> {
2336 run_path_with_cstr(p, &remove_dir_all_modern)
2337 }
2338}