1#![allow(nonstandard_style)]
2#![allow(unsafe_op_in_unsafe_fn)]
3// miri has some special hacks here that make things unused.
4#![cfg_attr(miri, allow(unused))]
5
6#[cfg(test)]
7mod tests;
8
9#[cfg(all(target_os = "linux", target_env = "gnu"))]
10use libc::c_char;
11#[cfg(any(
12 all(target_os = "linux", not(target_env = "musl")),
13 target_os = "android",
14 target_os = "fuchsia",
15 target_os = "hurd",
16 target_os = "illumos",
17))]
18use libc::dirfd;
19#[cfg(any(target_os = "fuchsia", target_os = "illumos"))]
20use libc::fstatat as fstatat64;
21#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))]
22use libc::fstatat64;
23#[cfg(any(
24 target_os = "android",
25 target_os = "solaris",
26 target_os = "fuchsia",
27 target_os = "redox",
28 target_os = "illumos",
29 target_os = "aix",
30 target_os = "nto",
31 target_os = "vita",
32 all(target_os = "linux", target_env = "musl"),
33))]
34use libc::readdir as readdir64;
35#[cfg(not(any(
36 target_os = "android",
37 target_os = "linux",
38 target_os = "solaris",
39 target_os = "illumos",
40 target_os = "l4re",
41 target_os = "fuchsia",
42 target_os = "redox",
43 target_os = "aix",
44 target_os = "nto",
45 target_os = "vita",
46 target_os = "hurd",
47)))]
48use libc::readdir_r as readdir64_r;
49#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))]
50use libc::readdir64;
51#[cfg(target_os = "l4re")]
52use libc::readdir64_r;
53use libc::{c_int, mode_t};
54#[cfg(target_os = "android")]
55use libc::{
56 dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64,
57 lstat as lstat64, off64_t, open as open64, stat as stat64,
58};
59#[cfg(not(any(
60 all(target_os = "linux", not(target_env = "musl")),
61 target_os = "l4re",
62 target_os = "android",
63 target_os = "hurd",
64)))]
65use libc::{
66 dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64,
67 lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
68};
69#[cfg(any(
70 all(target_os = "linux", not(target_env = "musl")),
71 target_os = "l4re",
72 target_os = "hurd"
73))]
74use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64};
75
76use crate::ffi::{CStr, OsStr, OsString};
77use crate::fmt::{self, Write as _};
78use crate::fs::TryLockError;
79use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
80use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
81use crate::os::unix::prelude::*;
82use crate::path::{Path, PathBuf};
83use crate::sync::Arc;
84use crate::sys::common::small_c_string::run_path_with_cstr;
85use crate::sys::fd::FileDesc;
86pub use crate::sys::fs::common::exists;
87use crate::sys::time::SystemTime;
88#[cfg(all(target_os = "linux", target_env = "gnu"))]
89use crate::sys::weak::syscall;
90#[cfg(target_os = "android")]
91use crate::sys::weak::weak;
92use crate::sys::{cvt, cvt_r};
93use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
94use crate::{mem, ptr};
95
96pub struct File(FileDesc);
97
98// FIXME: This should be available on Linux with all `target_env`.
99// But currently only glibc exposes `statx` fn and structs.
100// We don't want to import unverified raw C structs here directly.
101// https://github.com/rust-lang/rust/pull/67774
102macro_rules! cfg_has_statx {
103 ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
104 cfg_if::cfg_if! {
105 if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
106 $($then_tt)*
107 } else {
108 $($else_tt)*
109 }
110 }
111 };
112 ($($block_inner:tt)*) => {
113 #[cfg(all(target_os = "linux", target_env = "gnu"))]
114 {
115 $($block_inner)*
116 }
117 };
118}
119
120cfg_has_statx! {{
121 #[derive(Clone)]
122 pub struct FileAttr {
123 stat: stat64,
124 statx_extra_fields: Option<StatxExtraFields>,
125 }
126
127 #[derive(Clone)]
128 struct StatxExtraFields {
129 // This is needed to check if btime is supported by the filesystem.
130 stx_mask: u32,
131 stx_btime: libc::statx_timestamp,
132 // With statx, we can overcome 32-bit `time_t` too.
133 #[cfg(target_pointer_width = "32")]
134 stx_atime: libc::statx_timestamp,
135 #[cfg(target_pointer_width = "32")]
136 stx_ctime: libc::statx_timestamp,
137 #[cfg(target_pointer_width = "32")]
138 stx_mtime: libc::statx_timestamp,
139
140 }
141
142 // We prefer `statx` on Linux if available, which contains file creation time,
143 // as well as 64-bit timestamps of all kinds.
144 // Default `stat64` contains no creation time and may have 32-bit `time_t`.
145 unsafe fn try_statx(
146 fd: c_int,
147 path: *const c_char,
148 flags: i32,
149 mask: u32,
150 ) -> Option<io::Result<FileAttr>> {
151 use crate::sync::atomic::{Atomic, AtomicU8, Ordering};
152
153 // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`.
154 // We check for it on first failure and remember availability to avoid having to
155 // do it again.
156 #[repr(u8)]
157 enum STATX_STATE{ Unknown = 0, Present, Unavailable }
158 static STATX_SAVED_STATE: Atomic<u8> = AtomicU8::new(STATX_STATE::Unknown as u8);
159
160 syscall!(
161 fn statx(
162 fd: c_int,
163 pathname: *const c_char,
164 flags: c_int,
165 mask: libc::c_uint,
166 statxbuf: *mut libc::statx,
167 ) -> c_int;
168 );
169
170 let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed);
171 if statx_availability == STATX_STATE::Unavailable as u8 {
172 return None;
173 }
174
175 let mut buf: libc::statx = mem::zeroed();
176 if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
177 if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 {
178 return Some(Err(err));
179 }
180
181 // We're not yet entirely sure whether `statx` is usable on this kernel
182 // or not. Syscalls can return errors from things other than the kernel
183 // per se, e.g. `EPERM` can be returned if seccomp is used to block the
184 // syscall, or `ENOSYS` might be returned from a faulty FUSE driver.
185 //
186 // Availability is checked by performing a call which expects `EFAULT`
187 // if the syscall is usable.
188 //
189 // See: https://github.com/rust-lang/rust/issues/65662
190 //
191 // FIXME what about transient conditions like `ENOMEM`?
192 let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, ptr::null_mut()))
193 .err()
194 .and_then(|e| e.raw_os_error());
195 if err2 == Some(libc::EFAULT) {
196 STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
197 return Some(Err(err));
198 } else {
199 STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
200 return None;
201 }
202 }
203 if statx_availability == STATX_STATE::Unknown as u8 {
204 STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
205 }
206
207 // We cannot fill `stat64` exhaustively because of private padding fields.
208 let mut stat: stat64 = mem::zeroed();
209 // `c_ulong` on gnu-mips, `dev_t` otherwise
210 stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
211 stat.st_ino = buf.stx_ino as libc::ino64_t;
212 stat.st_nlink = buf.stx_nlink as libc::nlink_t;
213 stat.st_mode = buf.stx_mode as libc::mode_t;
214 stat.st_uid = buf.stx_uid as libc::uid_t;
215 stat.st_gid = buf.stx_gid as libc::gid_t;
216 stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
217 stat.st_size = buf.stx_size as off64_t;
218 stat.st_blksize = buf.stx_blksize as libc::blksize_t;
219 stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
220 stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
221 // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
222 stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
223 stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
224 stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
225 stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
226 stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
227
228 let extra = StatxExtraFields {
229 stx_mask: buf.stx_mask,
230 stx_btime: buf.stx_btime,
231 // Store full times to avoid 32-bit `time_t` truncation.
232 #[cfg(target_pointer_width = "32")]
233 stx_atime: buf.stx_atime,
234 #[cfg(target_pointer_width = "32")]
235 stx_ctime: buf.stx_ctime,
236 #[cfg(target_pointer_width = "32")]
237 stx_mtime: buf.stx_mtime,
238 };
239
240 Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
241 }
242
243} else {
244 #[derive(Clone)]
245 pub struct FileAttr {
246 stat: stat64,
247 }
248}}
249
250// all DirEntry's will have a reference to this struct
251struct InnerReadDir {
252 dirp: Dir,
253 root: PathBuf,
254}
255
256pub struct ReadDir {
257 inner: Arc<InnerReadDir>,
258 end_of_stream: bool,
259}
260
261impl ReadDir {
262 fn new(inner: InnerReadDir) -> Self {
263 Self { inner: Arc::new(data:inner), end_of_stream: false }
264 }
265}
266
267struct Dir(*mut libc::DIR);
268
269unsafe impl Send for Dir {}
270unsafe impl Sync for Dir {}
271
272#[cfg(any(
273 target_os = "android",
274 target_os = "linux",
275 target_os = "solaris",
276 target_os = "illumos",
277 target_os = "fuchsia",
278 target_os = "redox",
279 target_os = "aix",
280 target_os = "nto",
281 target_os = "vita",
282 target_os = "hurd",
283))]
284pub struct DirEntry {
285 dir: Arc<InnerReadDir>,
286 entry: dirent64_min,
287 // We need to store an owned copy of the entry name on platforms that use
288 // readdir() (not readdir_r()), because a) struct dirent may use a flexible
289 // array to store the name, b) it lives only until the next readdir() call.
290 name: crate::ffi::CString,
291}
292
293// Define a minimal subset of fields we need from `dirent64`, especially since
294// we're not using the immediate `d_name` on these targets. Keeping this as an
295// `entry` field in `DirEntry` helps reduce the `cfg` boilerplate elsewhere.
296#[cfg(any(
297 target_os = "android",
298 target_os = "linux",
299 target_os = "solaris",
300 target_os = "illumos",
301 target_os = "fuchsia",
302 target_os = "redox",
303 target_os = "aix",
304 target_os = "nto",
305 target_os = "vita",
306 target_os = "hurd",
307))]
308struct dirent64_min {
309 d_ino: u64,
310 #[cfg(not(any(
311 target_os = "solaris",
312 target_os = "illumos",
313 target_os = "aix",
314 target_os = "nto",
315 target_os = "vita",
316 )))]
317 d_type: u8,
318}
319
320#[cfg(not(any(
321 target_os = "android",
322 target_os = "linux",
323 target_os = "solaris",
324 target_os = "illumos",
325 target_os = "fuchsia",
326 target_os = "redox",
327 target_os = "aix",
328 target_os = "nto",
329 target_os = "vita",
330 target_os = "hurd",
331)))]
332pub struct DirEntry {
333 dir: Arc<InnerReadDir>,
334 // The full entry includes a fixed-length `d_name`.
335 entry: dirent64,
336}
337
338#[derive(Clone)]
339pub struct OpenOptions {
340 // generic
341 read: bool,
342 write: bool,
343 append: bool,
344 truncate: bool,
345 create: bool,
346 create_new: bool,
347 // system-specific
348 custom_flags: i32,
349 mode: mode_t,
350}
351
352#[derive(Clone, PartialEq, Eq)]
353pub struct FilePermissions {
354 mode: mode_t,
355}
356
357#[derive(Copy, Clone, Debug, Default)]
358pub struct FileTimes {
359 accessed: Option<SystemTime>,
360 modified: Option<SystemTime>,
361 #[cfg(target_vendor = "apple")]
362 created: Option<SystemTime>,
363}
364
365#[derive(Copy, Clone, Eq)]
366pub struct FileType {
367 mode: mode_t,
368}
369
370impl PartialEq for FileType {
371 fn eq(&self, other: &Self) -> bool {
372 self.masked() == other.masked()
373 }
374}
375
376impl core::hash::Hash for FileType {
377 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
378 self.masked().hash(state);
379 }
380}
381
382pub struct DirBuilder {
383 mode: mode_t,
384}
385
386#[derive(Copy, Clone)]
387struct Mode(mode_t);
388
389cfg_has_statx! {{
390 impl FileAttr {
391 fn from_stat64(stat: stat64) -> Self {
392 Self { stat, statx_extra_fields: None }
393 }
394
395 #[cfg(target_pointer_width = "32")]
396 pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> {
397 if let Some(ext) = &self.statx_extra_fields {
398 if (ext.stx_mask & libc::STATX_MTIME) != 0 {
399 return Some(&ext.stx_mtime);
400 }
401 }
402 None
403 }
404
405 #[cfg(target_pointer_width = "32")]
406 pub fn stx_atime(&self) -> Option<&libc::statx_timestamp> {
407 if let Some(ext) = &self.statx_extra_fields {
408 if (ext.stx_mask & libc::STATX_ATIME) != 0 {
409 return Some(&ext.stx_atime);
410 }
411 }
412 None
413 }
414
415 #[cfg(target_pointer_width = "32")]
416 pub fn stx_ctime(&self) -> Option<&libc::statx_timestamp> {
417 if let Some(ext) = &self.statx_extra_fields {
418 if (ext.stx_mask & libc::STATX_CTIME) != 0 {
419 return Some(&ext.stx_ctime);
420 }
421 }
422 None
423 }
424 }
425} else {
426 impl FileAttr {
427 fn from_stat64(stat: stat64) -> Self {
428 Self { stat }
429 }
430 }
431}}
432
433impl FileAttr {
434 pub fn size(&self) -> u64 {
435 self.stat.st_size as u64
436 }
437 pub fn perm(&self) -> FilePermissions {
438 FilePermissions { mode: (self.stat.st_mode as mode_t) }
439 }
440
441 pub fn file_type(&self) -> FileType {
442 FileType { mode: self.stat.st_mode as mode_t }
443 }
444}
445
446#[cfg(target_os = "netbsd")]
447impl FileAttr {
448 pub fn modified(&self) -> io::Result<SystemTime> {
449 SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64)
450 }
451
452 pub fn accessed(&self) -> io::Result<SystemTime> {
453 SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64)
454 }
455
456 pub fn created(&self) -> io::Result<SystemTime> {
457 SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64)
458 }
459}
460
461#[cfg(target_os = "aix")]
462impl FileAttr {
463 pub fn modified(&self) -> io::Result<SystemTime> {
464 SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64)
465 }
466
467 pub fn accessed(&self) -> io::Result<SystemTime> {
468 SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64)
469 }
470
471 pub fn created(&self) -> io::Result<SystemTime> {
472 SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64)
473 }
474}
475
476#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))]
477impl FileAttr {
478 #[cfg(not(any(
479 target_os = "vxworks",
480 target_os = "espidf",
481 target_os = "horizon",
482 target_os = "vita",
483 target_os = "hurd",
484 target_os = "rtems",
485 target_os = "nuttx",
486 )))]
487 pub fn modified(&self) -> io::Result<SystemTime> {
488 #[cfg(target_pointer_width = "32")]
489 cfg_has_statx! {
490 if let Some(mtime) = self.stx_mtime() {
491 return SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64);
492 }
493 }
494
495 SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64)
496 }
497
498 #[cfg(any(
499 target_os = "vxworks",
500 target_os = "espidf",
501 target_os = "vita",
502 target_os = "rtems",
503 ))]
504 pub fn modified(&self) -> io::Result<SystemTime> {
505 SystemTime::new(self.stat.st_mtime as i64, 0)
506 }
507
508 #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))]
509 pub fn modified(&self) -> io::Result<SystemTime> {
510 SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64)
511 }
512
513 #[cfg(not(any(
514 target_os = "vxworks",
515 target_os = "espidf",
516 target_os = "horizon",
517 target_os = "vita",
518 target_os = "hurd",
519 target_os = "rtems",
520 target_os = "nuttx",
521 )))]
522 pub fn accessed(&self) -> io::Result<SystemTime> {
523 #[cfg(target_pointer_width = "32")]
524 cfg_has_statx! {
525 if let Some(atime) = self.stx_atime() {
526 return SystemTime::new(atime.tv_sec, atime.tv_nsec as i64);
527 }
528 }
529
530 SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64)
531 }
532
533 #[cfg(any(
534 target_os = "vxworks",
535 target_os = "espidf",
536 target_os = "vita",
537 target_os = "rtems"
538 ))]
539 pub fn accessed(&self) -> io::Result<SystemTime> {
540 SystemTime::new(self.stat.st_atime as i64, 0)
541 }
542
543 #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))]
544 pub fn accessed(&self) -> io::Result<SystemTime> {
545 SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64)
546 }
547
548 #[cfg(any(
549 target_os = "freebsd",
550 target_os = "openbsd",
551 target_vendor = "apple",
552 target_os = "cygwin",
553 ))]
554 pub fn created(&self) -> io::Result<SystemTime> {
555 SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64)
556 }
557
558 #[cfg(not(any(
559 target_os = "freebsd",
560 target_os = "openbsd",
561 target_os = "vita",
562 target_vendor = "apple",
563 target_os = "cygwin",
564 )))]
565 pub fn created(&self) -> io::Result<SystemTime> {
566 cfg_has_statx! {
567 if let Some(ext) = &self.statx_extra_fields {
568 return if (ext.stx_mask & libc::STATX_BTIME) != 0 {
569 SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64)
570 } else {
571 Err(io::const_error!(
572 io::ErrorKind::Unsupported,
573 "creation time is not available for the filesystem",
574 ))
575 };
576 }
577 }
578
579 Err(io::const_error!(
580 io::ErrorKind::Unsupported,
581 "creation time is not available on this platform currently",
582 ))
583 }
584
585 #[cfg(target_os = "vita")]
586 pub fn created(&self) -> io::Result<SystemTime> {
587 SystemTime::new(self.stat.st_ctime as i64, 0)
588 }
589}
590
591#[cfg(target_os = "nto")]
592impl FileAttr {
593 pub fn modified(&self) -> io::Result<SystemTime> {
594 SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec)
595 }
596
597 pub fn accessed(&self) -> io::Result<SystemTime> {
598 SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec)
599 }
600
601 pub fn created(&self) -> io::Result<SystemTime> {
602 SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec)
603 }
604}
605
606impl AsInner<stat64> for FileAttr {
607 #[inline]
608 fn as_inner(&self) -> &stat64 {
609 &self.stat
610 }
611}
612
613impl FilePermissions {
614 pub fn readonly(&self) -> bool {
615 // check if any class (owner, group, others) has write permission
616 self.mode & 0o222 == 0
617 }
618
619 pub fn set_readonly(&mut self, readonly: bool) {
620 if readonly {
621 // remove write permission for all classes; equivalent to `chmod a-w <file>`
622 self.mode &= !0o222;
623 } else {
624 // add write permission for all classes; equivalent to `chmod a+w <file>`
625 self.mode |= 0o222;
626 }
627 }
628 pub fn mode(&self) -> u32 {
629 self.mode as u32
630 }
631}
632
633impl FileTimes {
634 pub fn set_accessed(&mut self, t: SystemTime) {
635 self.accessed = Some(t);
636 }
637
638 pub fn set_modified(&mut self, t: SystemTime) {
639 self.modified = Some(t);
640 }
641
642 #[cfg(target_vendor = "apple")]
643 pub fn set_created(&mut self, t: SystemTime) {
644 self.created = Some(t);
645 }
646}
647
648impl FileType {
649 pub fn is_dir(&self) -> bool {
650 self.is(mode:libc::S_IFDIR)
651 }
652 pub fn is_file(&self) -> bool {
653 self.is(mode:libc::S_IFREG)
654 }
655 pub fn is_symlink(&self) -> bool {
656 self.is(mode:libc::S_IFLNK)
657 }
658
659 pub fn is(&self, mode: mode_t) -> bool {
660 self.masked() == mode
661 }
662
663 fn masked(&self) -> mode_t {
664 self.mode & libc::S_IFMT
665 }
666}
667
668impl fmt::Debug for FileType {
669 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670 let FileType { mode: &u32 } = self;
671 f.debug_struct("FileType").field(name:"mode", &Mode(*mode)).finish()
672 }
673}
674
675impl FromInner<u32> for FilePermissions {
676 fn from_inner(mode: u32) -> FilePermissions {
677 FilePermissions { mode: mode as mode_t }
678 }
679}
680
681impl fmt::Debug for FilePermissions {
682 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
683 let FilePermissions { mode: &u32 } = self;
684 f.debug_struct("FilePermissions").field(name:"mode", &Mode(*mode)).finish()
685 }
686}
687
688impl fmt::Debug for ReadDir {
689 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
691 // Thus the result will be e g 'ReadDir("/home")'
692 fmt::Debug::fmt(&*self.inner.root, f)
693 }
694}
695
696impl Iterator for ReadDir {
697 type Item = io::Result<DirEntry>;
698
699 #[cfg(any(
700 target_os = "android",
701 target_os = "linux",
702 target_os = "solaris",
703 target_os = "fuchsia",
704 target_os = "redox",
705 target_os = "illumos",
706 target_os = "aix",
707 target_os = "nto",
708 target_os = "vita",
709 target_os = "hurd",
710 ))]
711 fn next(&mut self) -> Option<io::Result<DirEntry>> {
712 use crate::sys::os::{errno, set_errno};
713
714 if self.end_of_stream {
715 return None;
716 }
717
718 unsafe {
719 loop {
720 // As of POSIX.1-2017, readdir() is not required to be thread safe; only
721 // readdir_r() is. However, readdir_r() cannot correctly handle platforms
722 // with unlimited or variable NAME_MAX. Many modern platforms guarantee
723 // thread safety for readdir() as long an individual DIR* is not accessed
724 // concurrently, which is sufficient for Rust.
725 set_errno(0);
726 let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0);
727 if entry_ptr.is_null() {
728 // We either encountered an error, or reached the end. Either way,
729 // the next call to next() should return None.
730 self.end_of_stream = true;
731
732 // To distinguish between errors and end-of-directory, we had to clear
733 // errno beforehand to check for an error now.
734 return match errno() {
735 0 => None,
736 e => Some(Err(Error::from_raw_os_error(e))),
737 };
738 }
739
740 // The dirent64 struct is a weird imaginary thing that isn't ever supposed
741 // to be worked with by value. Its trailing d_name field is declared
742 // variously as [c_char; 256] or [c_char; 1] on different systems but
743 // either way that size is meaningless; only the offset of d_name is
744 // meaningful. The dirent64 pointers that libc returns from readdir64 are
745 // allowed to point to allocations smaller _or_ LARGER than implied by the
746 // definition of the struct.
747 //
748 // As such, we need to be even more careful with dirent64 than if its
749 // contents were "simply" partially initialized data.
750 //
751 // Like for uninitialized contents, converting entry_ptr to `&dirent64`
752 // would not be legal. However, we can use `&raw const (*entry_ptr).d_name`
753 // to refer the fields individually, because that operation is equivalent
754 // to `byte_offset` and thus does not require the full extent of `*entry_ptr`
755 // to be in bounds of the same allocation, only the offset of the field
756 // being referenced.
757
758 // d_name is guaranteed to be null-terminated.
759 let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast());
760 let name_bytes = name.to_bytes();
761 if name_bytes == b"." || name_bytes == b".." {
762 continue;
763 }
764
765 // When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as
766 // a value expression will do the right thing: `byte_offset` to the field and then
767 // only access those bytes.
768 #[cfg(not(target_os = "vita"))]
769 let entry = dirent64_min {
770 d_ino: (*entry_ptr).d_ino as u64,
771 #[cfg(not(any(
772 target_os = "solaris",
773 target_os = "illumos",
774 target_os = "aix",
775 target_os = "nto",
776 )))]
777 d_type: (*entry_ptr).d_type as u8,
778 };
779
780 #[cfg(target_os = "vita")]
781 let entry = dirent64_min { d_ino: 0u64 };
782
783 return Some(Ok(DirEntry {
784 entry,
785 name: name.to_owned(),
786 dir: Arc::clone(&self.inner),
787 }));
788 }
789 }
790 }
791
792 #[cfg(not(any(
793 target_os = "android",
794 target_os = "linux",
795 target_os = "solaris",
796 target_os = "fuchsia",
797 target_os = "redox",
798 target_os = "illumos",
799 target_os = "aix",
800 target_os = "nto",
801 target_os = "vita",
802 target_os = "hurd",
803 )))]
804 fn next(&mut self) -> Option<io::Result<DirEntry>> {
805 if self.end_of_stream {
806 return None;
807 }
808
809 unsafe {
810 let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
811 let mut entry_ptr = ptr::null_mut();
812 loop {
813 let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr);
814 if err != 0 {
815 if entry_ptr.is_null() {
816 // We encountered an error (which will be returned in this iteration), but
817 // we also reached the end of the directory stream. The `end_of_stream`
818 // flag is enabled to make sure that we return `None` in the next iteration
819 // (instead of looping forever)
820 self.end_of_stream = true;
821 }
822 return Some(Err(Error::from_raw_os_error(err)));
823 }
824 if entry_ptr.is_null() {
825 return None;
826 }
827 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
828 return Some(Ok(ret));
829 }
830 }
831 }
832 }
833}
834
835/// Aborts the process if a file desceriptor is not open, if debug asserts are enabled
836///
837/// Many IO syscalls can't be fully trusted about EBADF error codes because those
838/// might get bubbled up from a remote FUSE server rather than the file descriptor
839/// in the current process being invalid.
840///
841/// So we check file flags instead which live on the file descriptor and not the underlying file.
842/// The downside is that it costs an extra syscall, so we only do it for debug.
843#[inline]
844pub(crate) fn debug_assert_fd_is_open(fd: RawFd) {
845 use crate::sys::os::errno;
846
847 // this is similar to assert_unsafe_precondition!() but it doesn't require const
848 if core::ub_checks::check_library_ub() {
849 if unsafe { libc::fcntl(fd, cmd:libc::F_GETFD) } == -1 && errno() == libc::EBADF {
850 rtabort!("IO Safety violation: owned file descriptor already closed");
851 }
852 }
853}
854
855impl Drop for Dir {
856 fn drop(&mut self) {
857 // dirfd isn't supported everywhere
858 #[cfg(not(any(
859 miri,
860 target_os = "redox",
861 target_os = "nto",
862 target_os = "vita",
863 target_os = "hurd",
864 target_os = "espidf",
865 target_os = "horizon",
866 target_os = "vxworks",
867 target_os = "rtems",
868 target_os = "nuttx",
869 )))]
870 {
871 let fd = unsafe { libc::dirfd(self.0) };
872 debug_assert_fd_is_open(fd);
873 }
874 let r = unsafe { libc::closedir(self.0) };
875 assert!(
876 r == 0 || crate::io::Error::last_os_error().is_interrupted(),
877 "unexpected error during closedir: {:?}",
878 crate::io::Error::last_os_error()
879 );
880 }
881}
882
883impl DirEntry {
884 pub fn path(&self) -> PathBuf {
885 self.dir.root.join(self.file_name_os_str())
886 }
887
888 pub fn file_name(&self) -> OsString {
889 self.file_name_os_str().to_os_string()
890 }
891
892 #[cfg(all(
893 any(
894 all(target_os = "linux", not(target_env = "musl")),
895 target_os = "android",
896 target_os = "fuchsia",
897 target_os = "hurd",
898 target_os = "illumos",
899 ),
900 not(miri) // no dirfd on Miri
901 ))]
902 pub fn metadata(&self) -> io::Result<FileAttr> {
903 let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
904 let name = self.name_cstr().as_ptr();
905
906 cfg_has_statx! {
907 if let Some(ret) = unsafe { try_statx(
908 fd,
909 name,
910 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
911 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
912 ) } {
913 return ret;
914 }
915 }
916
917 let mut stat: stat64 = unsafe { mem::zeroed() };
918 cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
919 Ok(FileAttr::from_stat64(stat))
920 }
921
922 #[cfg(any(
923 not(any(
924 all(target_os = "linux", not(target_env = "musl")),
925 target_os = "android",
926 target_os = "fuchsia",
927 target_os = "hurd",
928 target_os = "illumos",
929 )),
930 miri
931 ))]
932 pub fn metadata(&self) -> io::Result<FileAttr> {
933 run_path_with_cstr(&self.path(), &lstat)
934 }
935
936 #[cfg(any(
937 target_os = "solaris",
938 target_os = "illumos",
939 target_os = "haiku",
940 target_os = "vxworks",
941 target_os = "aix",
942 target_os = "nto",
943 target_os = "vita",
944 ))]
945 pub fn file_type(&self) -> io::Result<FileType> {
946 self.metadata().map(|m| m.file_type())
947 }
948
949 #[cfg(not(any(
950 target_os = "solaris",
951 target_os = "illumos",
952 target_os = "haiku",
953 target_os = "vxworks",
954 target_os = "aix",
955 target_os = "nto",
956 target_os = "vita",
957 )))]
958 pub fn file_type(&self) -> io::Result<FileType> {
959 match self.entry.d_type {
960 libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
961 libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
962 libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
963 libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
964 libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
965 libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
966 libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
967 _ => self.metadata().map(|m| m.file_type()),
968 }
969 }
970
971 #[cfg(any(
972 target_os = "linux",
973 target_os = "cygwin",
974 target_os = "emscripten",
975 target_os = "android",
976 target_os = "solaris",
977 target_os = "illumos",
978 target_os = "haiku",
979 target_os = "l4re",
980 target_os = "fuchsia",
981 target_os = "redox",
982 target_os = "vxworks",
983 target_os = "espidf",
984 target_os = "horizon",
985 target_os = "vita",
986 target_os = "aix",
987 target_os = "nto",
988 target_os = "hurd",
989 target_os = "rtems",
990 target_vendor = "apple",
991 ))]
992 pub fn ino(&self) -> u64 {
993 self.entry.d_ino as u64
994 }
995
996 #[cfg(any(
997 target_os = "freebsd",
998 target_os = "openbsd",
999 target_os = "netbsd",
1000 target_os = "dragonfly"
1001 ))]
1002 pub fn ino(&self) -> u64 {
1003 self.entry.d_fileno as u64
1004 }
1005
1006 #[cfg(target_os = "nuttx")]
1007 pub fn ino(&self) -> u64 {
1008 // Leave this 0 for now, as NuttX does not provide an inode number
1009 // in its directory entries.
1010 0
1011 }
1012
1013 #[cfg(any(
1014 target_os = "netbsd",
1015 target_os = "openbsd",
1016 target_os = "freebsd",
1017 target_os = "dragonfly",
1018 target_vendor = "apple",
1019 ))]
1020 fn name_bytes(&self) -> &[u8] {
1021 use crate::slice;
1022 unsafe {
1023 slice::from_raw_parts(
1024 self.entry.d_name.as_ptr() as *const u8,
1025 self.entry.d_namlen as usize,
1026 )
1027 }
1028 }
1029 #[cfg(not(any(
1030 target_os = "netbsd",
1031 target_os = "openbsd",
1032 target_os = "freebsd",
1033 target_os = "dragonfly",
1034 target_vendor = "apple",
1035 )))]
1036 fn name_bytes(&self) -> &[u8] {
1037 self.name_cstr().to_bytes()
1038 }
1039
1040 #[cfg(not(any(
1041 target_os = "android",
1042 target_os = "linux",
1043 target_os = "solaris",
1044 target_os = "illumos",
1045 target_os = "fuchsia",
1046 target_os = "redox",
1047 target_os = "aix",
1048 target_os = "nto",
1049 target_os = "vita",
1050 target_os = "hurd",
1051 )))]
1052 fn name_cstr(&self) -> &CStr {
1053 unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
1054 }
1055 #[cfg(any(
1056 target_os = "android",
1057 target_os = "linux",
1058 target_os = "solaris",
1059 target_os = "illumos",
1060 target_os = "fuchsia",
1061 target_os = "redox",
1062 target_os = "aix",
1063 target_os = "nto",
1064 target_os = "vita",
1065 target_os = "hurd",
1066 ))]
1067 fn name_cstr(&self) -> &CStr {
1068 &self.name
1069 }
1070
1071 pub fn file_name_os_str(&self) -> &OsStr {
1072 OsStr::from_bytes(self.name_bytes())
1073 }
1074}
1075
1076impl OpenOptions {
1077 pub fn new() -> OpenOptions {
1078 OpenOptions {
1079 // generic
1080 read: false,
1081 write: false,
1082 append: false,
1083 truncate: false,
1084 create: false,
1085 create_new: false,
1086 // system-specific
1087 custom_flags: 0,
1088 mode: 0o666,
1089 }
1090 }
1091
1092 pub fn read(&mut self, read: bool) {
1093 self.read = read;
1094 }
1095 pub fn write(&mut self, write: bool) {
1096 self.write = write;
1097 }
1098 pub fn append(&mut self, append: bool) {
1099 self.append = append;
1100 }
1101 pub fn truncate(&mut self, truncate: bool) {
1102 self.truncate = truncate;
1103 }
1104 pub fn create(&mut self, create: bool) {
1105 self.create = create;
1106 }
1107 pub fn create_new(&mut self, create_new: bool) {
1108 self.create_new = create_new;
1109 }
1110
1111 pub fn custom_flags(&mut self, flags: i32) {
1112 self.custom_flags = flags;
1113 }
1114 pub fn mode(&mut self, mode: u32) {
1115 self.mode = mode as mode_t;
1116 }
1117
1118 fn get_access_mode(&self) -> io::Result<c_int> {
1119 match (self.read, self.write, self.append) {
1120 (true, false, false) => Ok(libc::O_RDONLY),
1121 (false, true, false) => Ok(libc::O_WRONLY),
1122 (true, true, false) => Ok(libc::O_RDWR),
1123 (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
1124 (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
1125 (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
1126 }
1127 }
1128
1129 fn get_creation_mode(&self) -> io::Result<c_int> {
1130 match (self.write, self.append) {
1131 (true, false) => {}
1132 (false, false) => {
1133 if self.truncate || self.create || self.create_new {
1134 return Err(Error::from_raw_os_error(libc::EINVAL));
1135 }
1136 }
1137 (_, true) => {
1138 if self.truncate && !self.create_new {
1139 return Err(Error::from_raw_os_error(libc::EINVAL));
1140 }
1141 }
1142 }
1143
1144 Ok(match (self.create, self.truncate, self.create_new) {
1145 (false, false, false) => 0,
1146 (true, false, false) => libc::O_CREAT,
1147 (false, true, false) => libc::O_TRUNC,
1148 (true, true, false) => libc::O_CREAT | libc::O_TRUNC,
1149 (_, _, true) => libc::O_CREAT | libc::O_EXCL,
1150 })
1151 }
1152}
1153
1154impl fmt::Debug for OpenOptions {
1155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1156 let OpenOptions { read: &bool, write: &bool, append: &bool, truncate: &bool, create: &bool, create_new: &bool, custom_flags: &i32, mode: &u32 } =
1157 self;
1158 f&mut DebugStruct<'_, '_>.debug_struct("OpenOptions")
1159 .field("read", read)
1160 .field("write", write)
1161 .field("append", append)
1162 .field("truncate", truncate)
1163 .field("create", create)
1164 .field("create_new", create_new)
1165 .field("custom_flags", custom_flags)
1166 .field(name:"mode", &Mode(*mode))
1167 .finish()
1168 }
1169}
1170
1171impl File {
1172 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
1173 run_path_with_cstr(path, &|path| File::open_c(path, opts))
1174 }
1175
1176 pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
1177 let flags = libc::O_CLOEXEC
1178 | opts.get_access_mode()?
1179 | opts.get_creation_mode()?
1180 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
1181 // The third argument of `open64` is documented to have type `mode_t`. On
1182 // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
1183 // However, since this is a variadic function, C integer promotion rules mean that on
1184 // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
1185 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
1186 Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
1187 }
1188
1189 pub fn file_attr(&self) -> io::Result<FileAttr> {
1190 let fd = self.as_raw_fd();
1191
1192 cfg_has_statx! {
1193 if let Some(ret) = unsafe { try_statx(
1194 fd,
1195 c"".as_ptr() as *const c_char,
1196 libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
1197 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
1198 ) } {
1199 return ret;
1200 }
1201 }
1202
1203 let mut stat: stat64 = unsafe { mem::zeroed() };
1204 cvt(unsafe { fstat64(fd, &mut stat) })?;
1205 Ok(FileAttr::from_stat64(stat))
1206 }
1207
1208 pub fn fsync(&self) -> io::Result<()> {
1209 cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
1210 return Ok(());
1211
1212 #[cfg(target_vendor = "apple")]
1213 unsafe fn os_fsync(fd: c_int) -> c_int {
1214 libc::fcntl(fd, libc::F_FULLFSYNC)
1215 }
1216 #[cfg(not(target_vendor = "apple"))]
1217 unsafe fn os_fsync(fd: c_int) -> c_int {
1218 libc::fsync(fd)
1219 }
1220 }
1221
1222 pub fn datasync(&self) -> io::Result<()> {
1223 cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
1224 return Ok(());
1225
1226 #[cfg(target_vendor = "apple")]
1227 unsafe fn os_datasync(fd: c_int) -> c_int {
1228 libc::fcntl(fd, libc::F_FULLFSYNC)
1229 }
1230 #[cfg(any(
1231 target_os = "freebsd",
1232 target_os = "fuchsia",
1233 target_os = "linux",
1234 target_os = "cygwin",
1235 target_os = "android",
1236 target_os = "netbsd",
1237 target_os = "openbsd",
1238 target_os = "nto",
1239 target_os = "hurd",
1240 ))]
1241 unsafe fn os_datasync(fd: c_int) -> c_int {
1242 libc::fdatasync(fd)
1243 }
1244 #[cfg(not(any(
1245 target_os = "android",
1246 target_os = "fuchsia",
1247 target_os = "freebsd",
1248 target_os = "linux",
1249 target_os = "cygwin",
1250 target_os = "netbsd",
1251 target_os = "openbsd",
1252 target_os = "nto",
1253 target_os = "hurd",
1254 target_vendor = "apple",
1255 )))]
1256 unsafe fn os_datasync(fd: c_int) -> c_int {
1257 libc::fsync(fd)
1258 }
1259 }
1260
1261 #[cfg(any(
1262 target_os = "freebsd",
1263 target_os = "fuchsia",
1264 target_os = "linux",
1265 target_os = "netbsd",
1266 target_vendor = "apple",
1267 ))]
1268 pub fn lock(&self) -> io::Result<()> {
1269 cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?;
1270 return Ok(());
1271 }
1272
1273 #[cfg(not(any(
1274 target_os = "freebsd",
1275 target_os = "fuchsia",
1276 target_os = "linux",
1277 target_os = "netbsd",
1278 target_vendor = "apple",
1279 )))]
1280 pub fn lock(&self) -> io::Result<()> {
1281 Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported"))
1282 }
1283
1284 #[cfg(any(
1285 target_os = "freebsd",
1286 target_os = "fuchsia",
1287 target_os = "linux",
1288 target_os = "netbsd",
1289 target_vendor = "apple",
1290 ))]
1291 pub fn lock_shared(&self) -> io::Result<()> {
1292 cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?;
1293 return Ok(());
1294 }
1295
1296 #[cfg(not(any(
1297 target_os = "freebsd",
1298 target_os = "fuchsia",
1299 target_os = "linux",
1300 target_os = "netbsd",
1301 target_vendor = "apple",
1302 )))]
1303 pub fn lock_shared(&self) -> io::Result<()> {
1304 Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported"))
1305 }
1306
1307 #[cfg(any(
1308 target_os = "freebsd",
1309 target_os = "fuchsia",
1310 target_os = "linux",
1311 target_os = "netbsd",
1312 target_vendor = "apple",
1313 ))]
1314 pub fn try_lock(&self) -> Result<(), TryLockError> {
1315 let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) });
1316 if let Err(err) = result {
1317 if err.kind() == io::ErrorKind::WouldBlock {
1318 Err(TryLockError::WouldBlock)
1319 } else {
1320 Err(TryLockError::Error(err))
1321 }
1322 } else {
1323 Ok(())
1324 }
1325 }
1326
1327 #[cfg(not(any(
1328 target_os = "freebsd",
1329 target_os = "fuchsia",
1330 target_os = "linux",
1331 target_os = "netbsd",
1332 target_vendor = "apple",
1333 )))]
1334 pub fn try_lock(&self) -> Result<(), TryLockError> {
1335 Err(TryLockError::Error(io::const_error!(
1336 io::ErrorKind::Unsupported,
1337 "try_lock() not supported"
1338 )))
1339 }
1340
1341 #[cfg(any(
1342 target_os = "freebsd",
1343 target_os = "fuchsia",
1344 target_os = "linux",
1345 target_os = "netbsd",
1346 target_vendor = "apple",
1347 ))]
1348 pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
1349 let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) });
1350 if let Err(err) = result {
1351 if err.kind() == io::ErrorKind::WouldBlock {
1352 Err(TryLockError::WouldBlock)
1353 } else {
1354 Err(TryLockError::Error(err))
1355 }
1356 } else {
1357 Ok(())
1358 }
1359 }
1360
1361 #[cfg(not(any(
1362 target_os = "freebsd",
1363 target_os = "fuchsia",
1364 target_os = "linux",
1365 target_os = "netbsd",
1366 target_vendor = "apple",
1367 )))]
1368 pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
1369 Err(TryLockError::Error(io::const_error!(
1370 io::ErrorKind::Unsupported,
1371 "try_lock_shared() not supported"
1372 )))
1373 }
1374
1375 #[cfg(any(
1376 target_os = "freebsd",
1377 target_os = "fuchsia",
1378 target_os = "linux",
1379 target_os = "netbsd",
1380 target_vendor = "apple",
1381 ))]
1382 pub fn unlock(&self) -> io::Result<()> {
1383 cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?;
1384 return Ok(());
1385 }
1386
1387 #[cfg(not(any(
1388 target_os = "freebsd",
1389 target_os = "fuchsia",
1390 target_os = "linux",
1391 target_os = "netbsd",
1392 target_vendor = "apple",
1393 )))]
1394 pub fn unlock(&self) -> io::Result<()> {
1395 Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported"))
1396 }
1397
1398 pub fn truncate(&self, size: u64) -> io::Result<()> {
1399 let size: off64_t =
1400 size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1401 cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
1402 }
1403
1404 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
1405 self.0.read(buf)
1406 }
1407
1408 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
1409 self.0.read_vectored(bufs)
1410 }
1411
1412 #[inline]
1413 pub fn is_read_vectored(&self) -> bool {
1414 self.0.is_read_vectored()
1415 }
1416
1417 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
1418 self.0.read_at(buf, offset)
1419 }
1420
1421 pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
1422 self.0.read_buf(cursor)
1423 }
1424
1425 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
1426 self.0.read_vectored_at(bufs, offset)
1427 }
1428
1429 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
1430 self.0.write(buf)
1431 }
1432
1433 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
1434 self.0.write_vectored(bufs)
1435 }
1436
1437 #[inline]
1438 pub fn is_write_vectored(&self) -> bool {
1439 self.0.is_write_vectored()
1440 }
1441
1442 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
1443 self.0.write_at(buf, offset)
1444 }
1445
1446 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
1447 self.0.write_vectored_at(bufs, offset)
1448 }
1449
1450 #[inline]
1451 pub fn flush(&self) -> io::Result<()> {
1452 Ok(())
1453 }
1454
1455 pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
1456 let (whence, pos) = match pos {
1457 // Casting to `i64` is fine, too large values will end up as
1458 // negative which will cause an error in `lseek64`.
1459 SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
1460 SeekFrom::End(off) => (libc::SEEK_END, off),
1461 SeekFrom::Current(off) => (libc::SEEK_CUR, off),
1462 };
1463 let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) })?;
1464 Ok(n as u64)
1465 }
1466
1467 pub fn size(&self) -> Option<io::Result<u64>> {
1468 match self.file_attr().map(|attr| attr.size()) {
1469 // Fall back to default implementation if the returned size is 0,
1470 // we might be in a proc mount.
1471 Ok(0) => None,
1472 result => Some(result),
1473 }
1474 }
1475
1476 pub fn tell(&self) -> io::Result<u64> {
1477 self.seek(SeekFrom::Current(0))
1478 }
1479
1480 pub fn duplicate(&self) -> io::Result<File> {
1481 self.0.duplicate().map(File)
1482 }
1483
1484 pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
1485 cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
1486 Ok(())
1487 }
1488
1489 pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
1490 #[cfg(not(any(
1491 target_os = "redox",
1492 target_os = "espidf",
1493 target_os = "horizon",
1494 target_os = "vxworks",
1495 target_os = "nuttx",
1496 )))]
1497 let to_timespec = |time: Option<SystemTime>| match time {
1498 Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts),
1499 Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!(
1500 io::ErrorKind::InvalidInput,
1501 "timestamp is too large to set as a file time",
1502 )),
1503 Some(_) => Err(io::const_error!(
1504 io::ErrorKind::InvalidInput,
1505 "timestamp is too small to set as a file time",
1506 )),
1507 None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }),
1508 };
1509 cfg_if::cfg_if! {
1510 if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx"))] {
1511 // Redox doesn't appear to support `UTIME_OMIT`.
1512 // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore
1513 // the same as for Redox.
1514 let _ = times;
1515 Err(io::const_error!(
1516 io::ErrorKind::Unsupported,
1517 "setting file times not supported",
1518 ))
1519 } else if #[cfg(target_vendor = "apple")] {
1520 let mut buf = [mem::MaybeUninit::<libc::timespec>::uninit(); 3];
1521 let mut num_times = 0;
1522 let mut attrlist: libc::attrlist = unsafe { mem::zeroed() };
1523 attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT;
1524 if times.created.is_some() {
1525 buf[num_times].write(to_timespec(times.created)?);
1526 num_times += 1;
1527 attrlist.commonattr |= libc::ATTR_CMN_CRTIME;
1528 }
1529 if times.modified.is_some() {
1530 buf[num_times].write(to_timespec(times.modified)?);
1531 num_times += 1;
1532 attrlist.commonattr |= libc::ATTR_CMN_MODTIME;
1533 }
1534 if times.accessed.is_some() {
1535 buf[num_times].write(to_timespec(times.accessed)?);
1536 num_times += 1;
1537 attrlist.commonattr |= libc::ATTR_CMN_ACCTIME;
1538 }
1539 cvt(unsafe { libc::fsetattrlist(
1540 self.as_raw_fd(),
1541 (&raw const attrlist).cast::<libc::c_void>().cast_mut(),
1542 buf.as_ptr().cast::<libc::c_void>().cast_mut(),
1543 num_times * size_of::<libc::timespec>(),
1544 0
1545 ) })?;
1546 Ok(())
1547 } else if #[cfg(target_os = "android")] {
1548 let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
1549 // futimens requires Android API level 19
1550 cvt(unsafe {
1551 weak!(
1552 fn futimens(fd: c_int, times: *const libc::timespec) -> c_int;
1553 );
1554 match futimens.get() {
1555 Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()),
1556 None => return Err(io::const_error!(
1557 io::ErrorKind::Unsupported,
1558 "setting file times requires Android API level >= 19",
1559 )),
1560 }
1561 })?;
1562 Ok(())
1563 } else {
1564 #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
1565 {
1566 use crate::sys::{time::__timespec64, weak::weak};
1567
1568 // Added in glibc 2.34
1569 weak!(
1570 fn __futimens64(fd: c_int, times: *const __timespec64) -> c_int;
1571 );
1572
1573 if let Some(futimens64) = __futimens64.get() {
1574 let to_timespec = |time: Option<SystemTime>| time.map(|time| time.t.to_timespec64())
1575 .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _));
1576 let times = [to_timespec(times.accessed), to_timespec(times.modified)];
1577 cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) })?;
1578 return Ok(());
1579 }
1580 }
1581 let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
1582 cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?;
1583 Ok(())
1584 }
1585 }
1586 }
1587}
1588
1589impl DirBuilder {
1590 pub fn new() -> DirBuilder {
1591 DirBuilder { mode: 0o777 }
1592 }
1593
1594 pub fn mkdir(&self, p: &Path) -> io::Result<()> {
1595 run_path_with_cstr(path:p, &|p: &CStr| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ()))
1596 }
1597
1598 pub fn set_mode(&mut self, mode: u32) {
1599 self.mode = mode as mode_t;
1600 }
1601}
1602
1603impl fmt::Debug for DirBuilder {
1604 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1605 let DirBuilder { mode: &u32 } = self;
1606 f.debug_struct("DirBuilder").field(name:"mode", &Mode(*mode)).finish()
1607 }
1608}
1609
1610impl AsInner<FileDesc> for File {
1611 #[inline]
1612 fn as_inner(&self) -> &FileDesc {
1613 &self.0
1614 }
1615}
1616
1617impl AsInnerMut<FileDesc> for File {
1618 #[inline]
1619 fn as_inner_mut(&mut self) -> &mut FileDesc {
1620 &mut self.0
1621 }
1622}
1623
1624impl IntoInner<FileDesc> for File {
1625 fn into_inner(self) -> FileDesc {
1626 self.0
1627 }
1628}
1629
1630impl FromInner<FileDesc> for File {
1631 fn from_inner(file_desc: FileDesc) -> Self {
1632 Self(file_desc)
1633 }
1634}
1635
1636impl AsFd for File {
1637 #[inline]
1638 fn as_fd(&self) -> BorrowedFd<'_> {
1639 self.0.as_fd()
1640 }
1641}
1642
1643impl AsRawFd for File {
1644 #[inline]
1645 fn as_raw_fd(&self) -> RawFd {
1646 self.0.as_raw_fd()
1647 }
1648}
1649
1650impl IntoRawFd for File {
1651 fn into_raw_fd(self) -> RawFd {
1652 self.0.into_raw_fd()
1653 }
1654}
1655
1656impl FromRawFd for File {
1657 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
1658 Self(FromRawFd::from_raw_fd(raw_fd))
1659 }
1660}
1661
1662impl fmt::Debug for File {
1663 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1664 #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))]
1665 fn get_path(fd: c_int) -> Option<PathBuf> {
1666 let mut p = PathBuf::from("/proc/self/fd");
1667 p.push(&fd.to_string());
1668 run_path_with_cstr(&p, &readlink).ok()
1669 }
1670
1671 #[cfg(any(target_vendor = "apple", target_os = "netbsd"))]
1672 fn get_path(fd: c_int) -> Option<PathBuf> {
1673 // FIXME: The use of PATH_MAX is generally not encouraged, but it
1674 // is inevitable in this case because Apple targets and NetBSD define `fcntl`
1675 // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
1676 // alternatives. If a better method is invented, it should be used
1677 // instead.
1678 let mut buf = vec![0; libc::PATH_MAX as usize];
1679 let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
1680 if n == -1 {
1681 cfg_if::cfg_if! {
1682 if #[cfg(target_os = "netbsd")] {
1683 // fallback to procfs as last resort
1684 let mut p = PathBuf::from("/proc/self/fd");
1685 p.push(&fd.to_string());
1686 return run_path_with_cstr(&p, &readlink).ok()
1687 } else {
1688 return None;
1689 }
1690 }
1691 }
1692 let l = buf.iter().position(|&c| c == 0).unwrap();
1693 buf.truncate(l as usize);
1694 buf.shrink_to_fit();
1695 Some(PathBuf::from(OsString::from_vec(buf)))
1696 }
1697
1698 #[cfg(target_os = "freebsd")]
1699 fn get_path(fd: c_int) -> Option<PathBuf> {
1700 let info = Box::<libc::kinfo_file>::new_zeroed();
1701 let mut info = unsafe { info.assume_init() };
1702 info.kf_structsize = size_of::<libc::kinfo_file>() as libc::c_int;
1703 let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) };
1704 if n == -1 {
1705 return None;
1706 }
1707 let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() };
1708 Some(PathBuf::from(OsString::from_vec(buf)))
1709 }
1710
1711 #[cfg(target_os = "vxworks")]
1712 fn get_path(fd: c_int) -> Option<PathBuf> {
1713 let mut buf = vec![0; libc::PATH_MAX as usize];
1714 let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
1715 if n == -1 {
1716 return None;
1717 }
1718 let l = buf.iter().position(|&c| c == 0).unwrap();
1719 buf.truncate(l as usize);
1720 Some(PathBuf::from(OsString::from_vec(buf)))
1721 }
1722
1723 #[cfg(not(any(
1724 target_os = "linux",
1725 target_os = "vxworks",
1726 target_os = "freebsd",
1727 target_os = "netbsd",
1728 target_os = "illumos",
1729 target_os = "solaris",
1730 target_vendor = "apple",
1731 )))]
1732 fn get_path(_fd: c_int) -> Option<PathBuf> {
1733 // FIXME(#24570): implement this for other Unix platforms
1734 None
1735 }
1736
1737 fn get_mode(fd: c_int) -> Option<(bool, bool)> {
1738 let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
1739 if mode == -1 {
1740 return None;
1741 }
1742 match mode & libc::O_ACCMODE {
1743 libc::O_RDONLY => Some((true, false)),
1744 libc::O_RDWR => Some((true, true)),
1745 libc::O_WRONLY => Some((false, true)),
1746 _ => None,
1747 }
1748 }
1749
1750 let fd = self.as_raw_fd();
1751 let mut b = f.debug_struct("File");
1752 b.field("fd", &fd);
1753 if let Some(path) = get_path(fd) {
1754 b.field("path", &path);
1755 }
1756 if let Some((read, write)) = get_mode(fd) {
1757 b.field("read", &read).field("write", &write);
1758 }
1759 b.finish()
1760 }
1761}
1762
1763// Format in octal, followed by the mode format used in `ls -l`.
1764//
1765// References:
1766// https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html
1767// https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html
1768// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
1769//
1770// Example:
1771// 0o100664 (-rw-rw-r--)
1772impl fmt::Debug for Mode {
1773 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1774 let Self(mode) = *self;
1775 write!(f, "0o{mode:06o}")?;
1776
1777 let entry_type = match mode & libc::S_IFMT {
1778 libc::S_IFDIR => 'd',
1779 libc::S_IFBLK => 'b',
1780 libc::S_IFCHR => 'c',
1781 libc::S_IFLNK => 'l',
1782 libc::S_IFIFO => 'p',
1783 libc::S_IFREG => '-',
1784 _ => return Ok(()),
1785 };
1786
1787 f.write_str(" (")?;
1788 f.write_char(entry_type)?;
1789
1790 // Owner permissions
1791 f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?;
1792 f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?;
1793 let owner_executable = mode & libc::S_IXUSR != 0;
1794 let setuid = mode as c_int & libc::S_ISUID as c_int != 0;
1795 f.write_char(match (owner_executable, setuid) {
1796 (true, true) => 's', // executable and setuid
1797 (false, true) => 'S', // setuid
1798 (true, false) => 'x', // executable
1799 (false, false) => '-',
1800 })?;
1801
1802 // Group permissions
1803 f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?;
1804 f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?;
1805 let group_executable = mode & libc::S_IXGRP != 0;
1806 let setgid = mode as c_int & libc::S_ISGID as c_int != 0;
1807 f.write_char(match (group_executable, setgid) {
1808 (true, true) => 's', // executable and setgid
1809 (false, true) => 'S', // setgid
1810 (true, false) => 'x', // executable
1811 (false, false) => '-',
1812 })?;
1813
1814 // Other permissions
1815 f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?;
1816 f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?;
1817 let other_executable = mode & libc::S_IXOTH != 0;
1818 let sticky = mode as c_int & libc::S_ISVTX as c_int != 0;
1819 f.write_char(match (entry_type, other_executable, sticky) {
1820 ('d', true, true) => 't', // searchable and restricted deletion
1821 ('d', false, true) => 'T', // restricted deletion
1822 (_, true, _) => 'x', // executable
1823 (_, false, _) => '-',
1824 })?;
1825
1826 f.write_char(')')
1827 }
1828}
1829
1830pub fn readdir(path: &Path) -> io::Result<ReadDir> {
1831 let ptr: *mut DIR = run_path_with_cstr(path, &|p: &CStr| unsafe { Ok(libc::opendir(dirname:p.as_ptr())) })?;
1832 if ptr.is_null() {
1833 Err(Error::last_os_error())
1834 } else {
1835 let root: PathBuf = path.to_path_buf();
1836 let inner: InnerReadDir = InnerReadDir { dirp: Dir(ptr), root };
1837 Ok(ReadDir::new(inner))
1838 }
1839}
1840
1841pub fn unlink(p: &CStr) -> io::Result<()> {
1842 cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ())
1843}
1844
1845pub fn rename(old: &CStr, new: &CStr) -> io::Result<()> {
1846 cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ())
1847}
1848
1849pub fn set_perm(p: &CStr, perm: FilePermissions) -> io::Result<()> {
1850 cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ())
1851}
1852
1853pub fn rmdir(p: &CStr) -> io::Result<()> {
1854 cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ())
1855}
1856
1857pub fn readlink(c_path: &CStr) -> io::Result<PathBuf> {
1858 let p = c_path.as_ptr();
1859
1860 let mut buf = Vec::with_capacity(256);
1861
1862 loop {
1863 let buf_read =
1864 cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
1865
1866 unsafe {
1867 buf.set_len(buf_read);
1868 }
1869
1870 if buf_read != buf.capacity() {
1871 buf.shrink_to_fit();
1872
1873 return Ok(PathBuf::from(OsString::from_vec(buf)));
1874 }
1875
1876 // Trigger the internal buffer resizing logic of `Vec` by requiring
1877 // more space than the current capacity. The length is guaranteed to be
1878 // the same as the capacity due to the if statement above.
1879 buf.reserve(1);
1880 }
1881}
1882
1883pub fn symlink(original: &CStr, link: &CStr) -> io::Result<()> {
1884 cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ())
1885}
1886
1887pub fn link(original: &CStr, link: &CStr) -> io::Result<()> {
1888 cfg_if::cfg_if! {
1889 if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70"))] {
1890 // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
1891 // it implementation-defined whether `link` follows symlinks, so rely on the
1892 // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
1893 // Android has `linkat` on newer versions, but we happen to know `link`
1894 // always has the correct behavior, so it's here as well.
1895 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1896 } else {
1897 // Where we can, use `linkat` instead of `link`; see the comment above
1898 // this one for details on why.
1899 cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
1900 }
1901 }
1902 Ok(())
1903}
1904
1905pub fn stat(p: &CStr) -> io::Result<FileAttr> {
1906 cfg_has_statx! {
1907 if let Some(ret) = unsafe { try_statx(
1908 libc::AT_FDCWD,
1909 p.as_ptr(),
1910 libc::AT_STATX_SYNC_AS_STAT,
1911 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
1912 ) } {
1913 return ret;
1914 }
1915 }
1916
1917 let mut stat: stat64 = unsafe { mem::zeroed() };
1918 cvt(unsafe { stat64(path:p.as_ptr(), &mut stat) })?;
1919 Ok(FileAttr::from_stat64(stat))
1920}
1921
1922pub fn lstat(p: &CStr) -> io::Result<FileAttr> {
1923 cfg_has_statx! {
1924 if let Some(ret) = unsafe { try_statx(
1925 libc::AT_FDCWD,
1926 p.as_ptr(),
1927 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
1928 libc::STATX_BASIC_STATS | libc::STATX_BTIME,
1929 ) } {
1930 return ret;
1931 }
1932 }
1933
1934 let mut stat: stat64 = unsafe { mem::zeroed() };
1935 cvt(unsafe { lstat64(path:p.as_ptr(), &mut stat) })?;
1936 Ok(FileAttr::from_stat64(stat))
1937}
1938
1939pub fn canonicalize(path: &CStr) -> io::Result<PathBuf> {
1940 let r: *mut i8 = unsafe { libc::realpath(pathname:path.as_ptr(), resolved:ptr::null_mut()) };
1941 if r.is_null() {
1942 return Err(io::Error::last_os_error());
1943 }
1944 Ok(PathBuf::from(OsString::from_vec(unsafe {
1945 let buf: Vec = CStr::from_ptr(r).to_bytes().to_vec();
1946 libc::free(r as *mut _);
1947 buf
1948 })))
1949}
1950
1951fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1952 use crate::fs::File;
1953 use crate::sys::fs::common::NOT_FILE_ERROR;
1954
1955 let reader: File = File::open(path:from)?;
1956 let metadata: Metadata = reader.metadata()?;
1957 if !metadata.is_file() {
1958 return Err(NOT_FILE_ERROR);
1959 }
1960 Ok((reader, metadata))
1961}
1962
1963#[cfg(target_os = "espidf")]
1964fn open_to_and_set_permissions(
1965 to: &Path,
1966 _reader_metadata: &crate::fs::Metadata,
1967) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1968 use crate::fs::OpenOptions;
1969 let writer = OpenOptions::new().open(to)?;
1970 let writer_metadata = writer.metadata()?;
1971 Ok((writer, writer_metadata))
1972}
1973
1974#[cfg(not(target_os = "espidf"))]
1975fn open_to_and_set_permissions(
1976 to: &Path,
1977 reader_metadata: &crate::fs::Metadata,
1978) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1979 use crate::fs::OpenOptions;
1980 use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
1981
1982 let perm: Permissions = reader_metadata.permissions();
1983 let writer: File = OpenOptions::new()
1984 // create the file with the correct mode right away
1985 .mode(perm.mode())
1986 .write(true)
1987 .create(true)
1988 .truncate(true)
1989 .open(path:to)?;
1990 let writer_metadata: Metadata = writer.metadata()?;
1991 // fchmod is broken on vita
1992 #[cfg(not(target_os = "vita"))]
1993 if writer_metadata.is_file() {
1994 // Set the correct file permissions, in case the file already existed.
1995 // Don't set the permissions on already existing non-files like
1996 // pipes/FIFOs or device nodes.
1997 writer.set_permissions(perm)?;
1998 }
1999 Ok((writer, writer_metadata))
2000}
2001
2002mod cfm {
2003 use crate::fs::{File, Metadata};
2004 use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write};
2005
2006 #[allow(dead_code)]
2007 pub struct CachedFileMetadata(pub File, pub Metadata);
2008
2009 impl Read for CachedFileMetadata {
2010 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
2011 self.0.read(buf)
2012 }
2013 fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
2014 self.0.read_vectored(bufs)
2015 }
2016 fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
2017 self.0.read_buf(cursor)
2018 }
2019 #[inline]
2020 fn is_read_vectored(&self) -> bool {
2021 self.0.is_read_vectored()
2022 }
2023 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
2024 self.0.read_to_end(buf)
2025 }
2026 fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
2027 self.0.read_to_string(buf)
2028 }
2029 }
2030 impl Write for CachedFileMetadata {
2031 fn write(&mut self, buf: &[u8]) -> Result<usize> {
2032 self.0.write(buf)
2033 }
2034 fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
2035 self.0.write_vectored(bufs)
2036 }
2037 #[inline]
2038 fn is_write_vectored(&self) -> bool {
2039 self.0.is_write_vectored()
2040 }
2041 #[inline]
2042 fn flush(&mut self) -> Result<()> {
2043 self.0.flush()
2044 }
2045 }
2046}
2047#[cfg(any(target_os = "linux", target_os = "android"))]
2048pub(crate) use cfm::CachedFileMetadata;
2049
2050#[cfg(not(target_vendor = "apple"))]
2051pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
2052 let (reader: File, reader_metadata: Metadata) = open_from(from)?;
2053 let (writer: File, writer_metadata: Metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
2054
2055 io::copy(
2056 &mut cfm::CachedFileMetadata(reader, reader_metadata),
2057 &mut cfm::CachedFileMetadata(writer, writer_metadata),
2058 )
2059}
2060
2061#[cfg(target_vendor = "apple")]
2062pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
2063 const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA;
2064
2065 struct FreeOnDrop(libc::copyfile_state_t);
2066 impl Drop for FreeOnDrop {
2067 fn drop(&mut self) {
2068 // The code below ensures that `FreeOnDrop` is never a null pointer
2069 unsafe {
2070 // `copyfile_state_free` returns -1 if the `to` or `from` files
2071 // cannot be closed. However, this is not considered an error.
2072 libc::copyfile_state_free(self.0);
2073 }
2074 }
2075 }
2076
2077 let (reader, reader_metadata) = open_from(from)?;
2078
2079 let clonefile_result = run_path_with_cstr(to, &|to| {
2080 cvt(unsafe { libc::fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) })
2081 });
2082 match clonefile_result {
2083 Ok(_) => return Ok(reader_metadata.len()),
2084 Err(e) => match e.raw_os_error() {
2085 // `fclonefileat` will fail on non-APFS volumes, if the
2086 // destination already exists, or if the source and destination
2087 // are on different devices. In all these cases `fcopyfile`
2088 // should succeed.
2089 Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
2090 _ => return Err(e),
2091 },
2092 }
2093
2094 // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
2095 let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
2096
2097 // We ensure that `FreeOnDrop` never contains a null pointer so it is
2098 // always safe to call `copyfile_state_free`
2099 let state = unsafe {
2100 let state = libc::copyfile_state_alloc();
2101 if state.is_null() {
2102 return Err(crate::io::Error::last_os_error());
2103 }
2104 FreeOnDrop(state)
2105 };
2106
2107 let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA };
2108
2109 cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
2110
2111 let mut bytes_copied: libc::off_t = 0;
2112 cvt(unsafe {
2113 libc::copyfile_state_get(
2114 state.0,
2115 libc::COPYFILE_STATE_COPIED as u32,
2116 (&raw mut bytes_copied) as *mut libc::c_void,
2117 )
2118 })?;
2119 Ok(bytes_copied as u64)
2120}
2121
2122pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
2123 run_path_with_cstr(path, &|path: &CStr| {
2124 cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
2125 .map(|_| ())
2126 })
2127}
2128
2129pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
2130 cvt(unsafe { libc::fchown(fd, owner:uid as libc::uid_t, group:gid as libc::gid_t) })?;
2131 Ok(())
2132}
2133
2134#[cfg(not(target_os = "vxworks"))]
2135pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
2136 run_path_with_cstr(path, &|path: &CStr| {
2137 cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
2138 .map(|_| ())
2139 })
2140}
2141
2142#[cfg(target_os = "vxworks")]
2143pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
2144 let (_, _, _) = (path, uid, gid);
2145 Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks"))
2146}
2147
2148#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
2149pub fn chroot(dir: &Path) -> io::Result<()> {
2150 run_path_with_cstr(path:dir, &|dir: &CStr| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ()))
2151}
2152
2153#[cfg(target_os = "vxworks")]
2154pub fn chroot(dir: &Path) -> io::Result<()> {
2155 let _ = dir;
2156 Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks"))
2157}
2158
2159pub fn mkfifo(path: &Path, mode: u32) -> io::Result<()> {
2160 run_path_with_cstr(path, &|path: &CStr| {
2161 cvt(unsafe { libc::mkfifo(path.as_ptr(), mode.try_into().unwrap()) }).map(|_| ())
2162 })
2163}
2164
2165pub use remove_dir_impl::remove_dir_all;
2166
2167// Fallback for REDOX, ESP-ID, Horizon, Vita, Vxworks and Miri
2168#[cfg(any(
2169 target_os = "redox",
2170 target_os = "espidf",
2171 target_os = "horizon",
2172 target_os = "vita",
2173 target_os = "nto",
2174 target_os = "vxworks",
2175 miri
2176))]
2177mod remove_dir_impl {
2178 pub use crate::sys::fs::common::remove_dir_all;
2179}
2180
2181// Modern implementation using openat(), unlinkat() and fdopendir()
2182#[cfg(not(any(
2183 target_os = "redox",
2184 target_os = "espidf",
2185 target_os = "horizon",
2186 target_os = "vita",
2187 target_os = "nto",
2188 target_os = "vxworks",
2189 miri
2190)))]
2191mod remove_dir_impl {
2192 #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
2193 use libc::{fdopendir, openat, unlinkat};
2194 #[cfg(all(target_os = "linux", target_env = "gnu"))]
2195 use libc::{fdopendir, openat64 as openat, unlinkat};
2196
2197 use super::{Dir, DirEntry, InnerReadDir, ReadDir, lstat};
2198 use crate::ffi::CStr;
2199 use crate::io;
2200 use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
2201 use crate::os::unix::prelude::{OwnedFd, RawFd};
2202 use crate::path::{Path, PathBuf};
2203 use crate::sys::common::small_c_string::run_path_with_cstr;
2204 use crate::sys::{cvt, cvt_r};
2205 use crate::sys_common::ignore_notfound;
2206
2207 pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
2208 let fd = cvt_r(|| unsafe {
2209 openat(
2210 parent_fd.unwrap_or(libc::AT_FDCWD),
2211 p.as_ptr(),
2212 libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
2213 )
2214 })?;
2215 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
2216 }
2217
2218 fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
2219 let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) };
2220 if ptr.is_null() {
2221 return Err(io::Error::last_os_error());
2222 }
2223 let dirp = Dir(ptr);
2224 // file descriptor is automatically closed by libc::closedir() now, so give up ownership
2225 let new_parent_fd = dir_fd.into_raw_fd();
2226 // a valid root is not needed because we do not call any functions involving the full path
2227 // of the `DirEntry`s.
2228 let dummy_root = PathBuf::new();
2229 let inner = InnerReadDir { dirp, root: dummy_root };
2230 Ok((ReadDir::new(inner), new_parent_fd))
2231 }
2232
2233 #[cfg(any(
2234 target_os = "solaris",
2235 target_os = "illumos",
2236 target_os = "haiku",
2237 target_os = "vxworks",
2238 target_os = "aix",
2239 ))]
2240 fn is_dir(_ent: &DirEntry) -> Option<bool> {
2241 None
2242 }
2243
2244 #[cfg(not(any(
2245 target_os = "solaris",
2246 target_os = "illumos",
2247 target_os = "haiku",
2248 target_os = "vxworks",
2249 target_os = "aix",
2250 )))]
2251 fn is_dir(ent: &DirEntry) -> Option<bool> {
2252 match ent.entry.d_type {
2253 libc::DT_UNKNOWN => None,
2254 libc::DT_DIR => Some(true),
2255 _ => Some(false),
2256 }
2257 }
2258
2259 fn is_enoent(result: &io::Result<()>) -> bool {
2260 if let Err(err) = result
2261 && matches!(err.raw_os_error(), Some(libc::ENOENT))
2262 {
2263 true
2264 } else {
2265 false
2266 }
2267 }
2268
2269 fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {
2270 // try opening as directory
2271 let fd = match openat_nofollow_dironly(parent_fd, &path) {
2272 Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => {
2273 // not a directory - don't traverse further
2274 // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR)
2275 return match parent_fd {
2276 // unlink...
2277 Some(parent_fd) => {
2278 cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }).map(drop)
2279 }
2280 // ...unless this was supposed to be the deletion root directory
2281 None => Err(err),
2282 };
2283 }
2284 result => result?,
2285 };
2286
2287 // open the directory passing ownership of the fd
2288 let (dir, fd) = fdreaddir(fd)?;
2289 for child in dir {
2290 let child = child?;
2291 let child_name = child.name_cstr();
2292 // we need an inner try block, because if one of these
2293 // directories has already been deleted, then we need to
2294 // continue the loop, not return ok.
2295 let result: io::Result<()> = try {
2296 match is_dir(&child) {
2297 Some(true) => {
2298 remove_dir_all_recursive(Some(fd), child_name)?;
2299 }
2300 Some(false) => {
2301 cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?;
2302 }
2303 None => {
2304 // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
2305 // if the process has the appropriate privileges. This however can causing orphaned
2306 // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
2307 // into it first instead of trying to unlink() it.
2308 remove_dir_all_recursive(Some(fd), child_name)?;
2309 }
2310 }
2311 };
2312 if result.is_err() && !is_enoent(&result) {
2313 return result;
2314 }
2315 }
2316
2317 // unlink the directory after removing its contents
2318 ignore_notfound(cvt(unsafe {
2319 unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR)
2320 }))?;
2321 Ok(())
2322 }
2323
2324 fn remove_dir_all_modern(p: &CStr) -> io::Result<()> {
2325 // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
2326 // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
2327 // into symlinks.
2328 let attr = lstat(p)?;
2329 if attr.file_type().is_symlink() {
2330 super::unlink(p)
2331 } else {
2332 remove_dir_all_recursive(None, &p)
2333 }
2334 }
2335
2336 pub fn remove_dir_all(p: &Path) -> io::Result<()> {
2337 run_path_with_cstr(p, &remove_dir_all_modern)
2338 }
2339}
2340