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