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