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