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