1 use crate::os::unix::prelude::*;
2
3 use crate::ffi::{CStr, CString, OsStr, OsString};
4 use crate::fmt;
5 use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom};
6 use crate::mem;
7 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
8 use crate::path::{Path, PathBuf};
9 use crate::ptr;
10 use crate::sync::Arc;
11 use crate::sys::fd::FileDesc;
12 use crate::sys::time::SystemTime;
13 use crate::sys::{cvt, cvt_r};
14 use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
15
16 #[cfg(any(
17 all(target_os = "linux", target_env = "gnu"),
18 target_os = "macos",
19 target_os = "ios",
20 ))]
21 use crate::sys::weak::syscall;
22 #[cfg(target_os = "macos")]
23 use crate::sys::weak::weak;
24
25 use libc::{c_int, mode_t};
26
27 #[cfg(any(
28 target_os = "macos",
29 target_os = "ios",
30 all(target_os = "linux", target_env = "gnu")
31 ))]
32 use libc::c_char;
33 #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
34 use libc::dirfd;
35 #[cfg(any(target_os = "linux", target_os = "emscripten"))]
36 use libc::fstatat64;
37 #[cfg(not(any(
38 target_os = "linux",
39 target_os = "emscripten",
40 target_os = "solaris",
41 target_os = "illumos",
42 target_os = "l4re",
43 target_os = "fuchsia",
44 target_os = "redox"
45 )))]
46 use libc::readdir_r as readdir64_r;
47 #[cfg(target_os = "android")]
48 use libc::{
49 dirent as dirent64, fstat as fstat64, fstatat as fstatat64, lseek64, lstat as lstat64,
50 open as open64, stat as stat64,
51 };
52 #[cfg(not(any(
53 target_os = "linux",
54 target_os = "emscripten",
55 target_os = "l4re",
56 target_os = "android"
57 )))]
58 use libc::{
59 dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64,
60 lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
61 };
62 #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
63 use libc::{
64 dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64,
65 };
66
67 pub use crate::sys_common::fs::try_exists;
68
69 pub struct File(FileDesc);
70
71 // FIXME: This should be available on Linux with all `target_env`.
72 // But currently only glibc exposes `statx` fn and structs.
73 // We don't want to import unverified raw C structs here directly.
74 // https://github.com/rust-lang/rust/pull/67774
75 macro_rules! cfg_has_statx {
76 ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
77 cfg_if::cfg_if! {
78 if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
79 $($then_tt)*
80 } else {
81 $($else_tt)*
82 }
83 }
84 };
85 ($($block_inner:tt)*) => {
86 #[cfg(all(target_os = "linux", target_env = "gnu"))]
87 {
88 $($block_inner)*
89 }
90 };
91 }
92
93 cfg_has_statx! {{
94 #[derive(Clone)]
95 pub struct FileAttr {
96 stat: stat64,
97 statx_extra_fields: Option<StatxExtraFields>,
98 }
99
100 #[derive(Clone)]
101 struct StatxExtraFields {
102 // This is needed to check if btime is supported by the filesystem.
103 stx_mask: u32,
104 stx_btime: libc::statx_timestamp,
105 }
106
107 // We prefer `statx` on Linux if available, which contains file creation time.
108 // Default `stat64` contains no creation time.
109 unsafe fn try_statx(
110 fd: c_int,
111 path: *const c_char,
112 flags: i32,
113 mask: u32,
114 ) -> Option<io::Result<FileAttr>> {
115 use crate::sync::atomic::{AtomicU8, Ordering};
116
117 // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
118 // We store the availability in global to avoid unnecessary syscalls.
119 // 0: Unknown
120 // 1: Not available
121 // 2: Available
122 static STATX_STATE: AtomicU8 = AtomicU8::new(0);
123 syscall! {
124 fn statx(
125 fd: c_int,
126 pathname: *const c_char,
127 flags: c_int,
128 mask: libc::c_uint,
129 statxbuf: *mut libc::statx
130 ) -> c_int
131 }
132
133 match STATX_STATE.load(Ordering::Relaxed) {
134 0 => {
135 // It is a trick to call `statx` with null pointers to check if the syscall
136 // is available. According to the manual, it is expected to fail with EFAULT.
137 // We do this mainly for performance, since it is nearly hundreds times
138 // faster than a normal successful call.
139 let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
140 .err()
141 .and_then(|e| e.raw_os_error());
142 // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited
143 // and returns `EPERM`. Listing all possible errors seems not a good idea.
144 // See: https://github.com/rust-lang/rust/issues/65662
145 if err != Some(libc::EFAULT) {
146 STATX_STATE.store(1, Ordering::Relaxed);
147 return None;
148 }
149 STATX_STATE.store(2, Ordering::Relaxed);
150 }
151 1 => return None,
152 _ => {}
153 }
154
155 let mut buf: libc::statx = mem::zeroed();
156 if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
157 return Some(Err(err));
158 }
159
160 // We cannot fill `stat64` exhaustively because of private padding fields.
161 let mut stat: stat64 = mem::zeroed();
162 // `c_ulong` on gnu-mips, `dev_t` otherwise
163 stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
164 stat.st_ino = buf.stx_ino as libc::ino64_t;
165 stat.st_nlink = buf.stx_nlink as libc::nlink_t;
166 stat.st_mode = buf.stx_mode as libc::mode_t;
167 stat.st_uid = buf.stx_uid as libc::uid_t;
168 stat.st_gid = buf.stx_gid as libc::gid_t;
169 stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
170 stat.st_size = buf.stx_size as off64_t;
171 stat.st_blksize = buf.stx_blksize as libc::blksize_t;
172 stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
173 stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
174 // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
175 stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
176 stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
177 stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
178 stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
179 stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
180
181 let extra = StatxExtraFields {
182 stx_mask: buf.stx_mask,
183 stx_btime: buf.stx_btime,
184 };
185
186 Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
187 }
188
189 } else {
190 #[derive(Clone)]
191 pub struct FileAttr {
192 stat: stat64,
193 }
194 }}
195
196 // all DirEntry's will have a reference to this struct
197 struct InnerReadDir {
198 dirp: Dir,
199 root: PathBuf,
200 }
201
202 pub struct ReadDir {
203 inner: Arc<InnerReadDir>,
204 #[cfg(not(any(
205 target_os = "solaris",
206 target_os = "illumos",
207 target_os = "fuchsia",
208 target_os = "redox",
209 )))]
210 end_of_stream: bool,
211 }
212
213 struct Dir(*mut libc::DIR);
214
215 unsafe impl Send for Dir {}
216 unsafe impl Sync for Dir {}
217
218 pub struct DirEntry {
219 entry: dirent64,
220 dir: Arc<InnerReadDir>,
221 // We need to store an owned copy of the entry name
222 // on Solaris and Fuchsia because a) it uses a zero-length
223 // array to store the name, b) its lifetime between readdir
224 // calls is not guaranteed.
225 #[cfg(any(
226 target_os = "solaris",
227 target_os = "illumos",
228 target_os = "fuchsia",
229 target_os = "redox"
230 ))]
231 name: CString,
232 }
233
234 #[derive(Clone, Debug)]
235 pub struct OpenOptions {
236 // generic
237 read: bool,
238 write: bool,
239 append: bool,
240 truncate: bool,
241 create: bool,
242 create_new: bool,
243 // system-specific
244 custom_flags: i32,
245 mode: mode_t,
246 }
247
248 #[derive(Clone, PartialEq, Eq, Debug)]
249 pub struct FilePermissions {
250 mode: mode_t,
251 }
252
253 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
254 pub struct FileType {
255 mode: mode_t,
256 }
257
258 #[derive(Debug)]
259 pub struct DirBuilder {
260 mode: mode_t,
261 }
262
263 cfg_has_statx! {{
264 impl FileAttr {
265 fn from_stat64(stat: stat64) -> Self {
266 Self { stat, statx_extra_fields: None }
267 }
268 }
269 } else {
270 impl FileAttr {
271 fn from_stat64(stat: stat64) -> Self {
272 Self { stat }
273 }
274 }
275 }}
276
277 impl FileAttr {
size(&self) -> u64278 pub fn size(&self) -> u64 {
279 self.stat.st_size as u64
280 }
perm(&self) -> FilePermissions281 pub fn perm(&self) -> FilePermissions {
282 FilePermissions { mode: (self.stat.st_mode as mode_t) }
283 }
284
file_type(&self) -> FileType285 pub fn file_type(&self) -> FileType {
286 FileType { mode: self.stat.st_mode as mode_t }
287 }
288 }
289
290 #[cfg(target_os = "netbsd")]
291 impl FileAttr {
modified(&self) -> io::Result<SystemTime>292 pub fn modified(&self) -> io::Result<SystemTime> {
293 Ok(SystemTime::from(libc::timespec {
294 tv_sec: self.stat.st_mtime as libc::time_t,
295 tv_nsec: self.stat.st_mtimensec as libc::c_long,
296 }))
297 }
298
accessed(&self) -> io::Result<SystemTime>299 pub fn accessed(&self) -> io::Result<SystemTime> {
300 Ok(SystemTime::from(libc::timespec {
301 tv_sec: self.stat.st_atime as libc::time_t,
302 tv_nsec: self.stat.st_atimensec as libc::c_long,
303 }))
304 }
305
created(&self) -> io::Result<SystemTime>306 pub fn created(&self) -> io::Result<SystemTime> {
307 Ok(SystemTime::from(libc::timespec {
308 tv_sec: self.stat.st_birthtime as libc::time_t,
309 tv_nsec: self.stat.st_birthtimensec as libc::c_long,
310 }))
311 }
312 }
313
314 #[cfg(not(target_os = "netbsd"))]
315 impl FileAttr {
316 #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
modified(&self) -> io::Result<SystemTime>317 pub fn modified(&self) -> io::Result<SystemTime> {
318 Ok(SystemTime::from(libc::timespec {
319 tv_sec: self.stat.st_mtime as libc::time_t,
320 tv_nsec: self.stat.st_mtime_nsec as _,
321 }))
322 }
323
324 #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
modified(&self) -> io::Result<SystemTime>325 pub fn modified(&self) -> io::Result<SystemTime> {
326 Ok(SystemTime::from(libc::timespec {
327 tv_sec: self.stat.st_mtime as libc::time_t,
328 tv_nsec: 0,
329 }))
330 }
331
332 #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
accessed(&self) -> io::Result<SystemTime>333 pub fn accessed(&self) -> io::Result<SystemTime> {
334 Ok(SystemTime::from(libc::timespec {
335 tv_sec: self.stat.st_atime as libc::time_t,
336 tv_nsec: self.stat.st_atime_nsec as _,
337 }))
338 }
339
340 #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
accessed(&self) -> io::Result<SystemTime>341 pub fn accessed(&self) -> io::Result<SystemTime> {
342 Ok(SystemTime::from(libc::timespec {
343 tv_sec: self.stat.st_atime as libc::time_t,
344 tv_nsec: 0,
345 }))
346 }
347
348 #[cfg(any(
349 target_os = "freebsd",
350 target_os = "openbsd",
351 target_os = "macos",
352 target_os = "ios"
353 ))]
created(&self) -> io::Result<SystemTime>354 pub fn created(&self) -> io::Result<SystemTime> {
355 Ok(SystemTime::from(libc::timespec {
356 tv_sec: self.stat.st_birthtime as libc::time_t,
357 tv_nsec: self.stat.st_birthtime_nsec as libc::c_long,
358 }))
359 }
360
361 #[cfg(not(any(
362 target_os = "freebsd",
363 target_os = "openbsd",
364 target_os = "macos",
365 target_os = "ios"
366 )))]
created(&self) -> io::Result<SystemTime>367 pub fn created(&self) -> io::Result<SystemTime> {
368 cfg_has_statx! {
369 if let Some(ext) = &self.statx_extra_fields {
370 return if (ext.stx_mask & libc::STATX_BTIME) != 0 {
371 Ok(SystemTime::from(libc::timespec {
372 tv_sec: ext.stx_btime.tv_sec as libc::time_t,
373 tv_nsec: ext.stx_btime.tv_nsec as _,
374 }))
375 } else {
376 Err(io::Error::new_const(
377 io::ErrorKind::Uncategorized,
378 &"creation time is not available for the filesystem",
379 ))
380 };
381 }
382 }
383
384 Err(io::Error::new_const(
385 io::ErrorKind::Unsupported,
386 &"creation time is not available on this platform \
387 currently",
388 ))
389 }
390 }
391
392 impl AsInner<stat64> for FileAttr {
as_inner(&self) -> &stat64393 fn as_inner(&self) -> &stat64 {
394 &self.stat
395 }
396 }
397
398 impl FilePermissions {
readonly(&self) -> bool399 pub fn readonly(&self) -> bool {
400 // check if any class (owner, group, others) has write permission
401 self.mode & 0o222 == 0
402 }
403
set_readonly(&mut self, readonly: bool)404 pub fn set_readonly(&mut self, readonly: bool) {
405 if readonly {
406 // remove write permission for all classes; equivalent to `chmod a-w <file>`
407 self.mode &= !0o222;
408 } else {
409 // add write permission for all classes; equivalent to `chmod a+w <file>`
410 self.mode |= 0o222;
411 }
412 }
mode(&self) -> u32413 pub fn mode(&self) -> u32 {
414 self.mode as u32
415 }
416 }
417
418 impl FileType {
is_dir(&self) -> bool419 pub fn is_dir(&self) -> bool {
420 self.is(libc::S_IFDIR)
421 }
is_file(&self) -> bool422 pub fn is_file(&self) -> bool {
423 self.is(libc::S_IFREG)
424 }
is_symlink(&self) -> bool425 pub fn is_symlink(&self) -> bool {
426 self.is(libc::S_IFLNK)
427 }
428
is(&self, mode: mode_t) -> bool429 pub fn is(&self, mode: mode_t) -> bool {
430 self.mode & libc::S_IFMT == mode
431 }
432 }
433
434 impl FromInner<u32> for FilePermissions {
from_inner(mode: u32) -> FilePermissions435 fn from_inner(mode: u32) -> FilePermissions {
436 FilePermissions { mode: mode as mode_t }
437 }
438 }
439
440 impl fmt::Debug for ReadDir {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
443 // Thus the result will be e g 'ReadDir("/home")'
444 fmt::Debug::fmt(&*self.inner.root, f)
445 }
446 }
447
448 impl Iterator for ReadDir {
449 type Item = io::Result<DirEntry>;
450
451 #[cfg(any(
452 target_os = "solaris",
453 target_os = "fuchsia",
454 target_os = "redox",
455 target_os = "illumos"
456 ))]
next(&mut self) -> Option<io::Result<DirEntry>>457 fn next(&mut self) -> Option<io::Result<DirEntry>> {
458 unsafe {
459 loop {
460 // Although readdir_r(3) would be a correct function to use here because
461 // of the thread safety, on Illumos and Fuchsia the readdir(3C) function
462 // is safe to use in threaded applications and it is generally preferred
463 // over the readdir_r(3C) function.
464 super::os::set_errno(0);
465 let entry_ptr = libc::readdir(self.inner.dirp.0);
466 if entry_ptr.is_null() {
467 // null can mean either the end is reached or an error occurred.
468 // So we had to clear errno beforehand to check for an error now.
469 return match super::os::errno() {
470 0 => None,
471 e => Some(Err(Error::from_raw_os_error(e))),
472 };
473 }
474
475 let ret = DirEntry {
476 entry: *entry_ptr,
477 // d_name is guaranteed to be null-terminated.
478 name: CStr::from_ptr((*entry_ptr).d_name.as_ptr()).to_owned(),
479 dir: Arc::clone(&self.inner),
480 };
481 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
482 return Some(Ok(ret));
483 }
484 }
485 }
486 }
487
488 #[cfg(not(any(
489 target_os = "solaris",
490 target_os = "fuchsia",
491 target_os = "redox",
492 target_os = "illumos"
493 )))]
next(&mut self) -> Option<io::Result<DirEntry>>494 fn next(&mut self) -> Option<io::Result<DirEntry>> {
495 if self.end_of_stream {
496 return None;
497 }
498
499 unsafe {
500 let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
501 let mut entry_ptr = ptr::null_mut();
502 loop {
503 let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr);
504 if err != 0 {
505 if entry_ptr.is_null() {
506 // We encountered an error (which will be returned in this iteration), but
507 // we also reached the end of the directory stream. The `end_of_stream`
508 // flag is enabled to make sure that we return `None` in the next iteration
509 // (instead of looping forever)
510 self.end_of_stream = true;
511 }
512 return Some(Err(Error::from_raw_os_error(err)));
513 }
514 if entry_ptr.is_null() {
515 return None;
516 }
517 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
518 return Some(Ok(ret));
519 }
520 }
521 }
522 }
523 }
524
525 impl Drop for Dir {
drop(&mut self)526 fn drop(&mut self) {
527 let r = unsafe { libc::closedir(self.0) };
528 debug_assert_eq!(r, 0);
529 }
530 }
531
532 impl DirEntry {
path(&self) -> PathBuf533 pub fn path(&self) -> PathBuf {
534 self.dir.root.join(OsStr::from_bytes(self.name_bytes()))
535 }
536
file_name(&self) -> OsString537 pub fn file_name(&self) -> OsString {
538 OsStr::from_bytes(self.name_bytes()).to_os_string()
539 }
540
541 #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
metadata(&self) -> io::Result<FileAttr>542 pub fn metadata(&self) -> io::Result<FileAttr> {
543 let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
544 let name = self.entry.d_name.as_ptr();
545
546 cfg_has_statx! {
547 if let Some(ret) = unsafe { try_statx(
548 fd,
549 name,
550 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
551 libc::STATX_ALL,
552 ) } {
553 return ret;
554 }
555 }
556
557 let mut stat: stat64 = unsafe { mem::zeroed() };
558 cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
559 Ok(FileAttr::from_stat64(stat))
560 }
561
562 #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
metadata(&self) -> io::Result<FileAttr>563 pub fn metadata(&self) -> io::Result<FileAttr> {
564 lstat(&self.path())
565 }
566
567 #[cfg(any(
568 target_os = "solaris",
569 target_os = "illumos",
570 target_os = "haiku",
571 target_os = "vxworks"
572 ))]
file_type(&self) -> io::Result<FileType>573 pub fn file_type(&self) -> io::Result<FileType> {
574 lstat(&self.path()).map(|m| m.file_type())
575 }
576
577 #[cfg(not(any(
578 target_os = "solaris",
579 target_os = "illumos",
580 target_os = "haiku",
581 target_os = "vxworks"
582 )))]
file_type(&self) -> io::Result<FileType>583 pub fn file_type(&self) -> io::Result<FileType> {
584 match self.entry.d_type {
585 libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
586 libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
587 libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
588 libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
589 libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
590 libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
591 libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
592 _ => lstat(&self.path()).map(|m| m.file_type()),
593 }
594 }
595
596 #[cfg(any(
597 target_os = "macos",
598 target_os = "ios",
599 target_os = "linux",
600 target_os = "emscripten",
601 target_os = "android",
602 target_os = "solaris",
603 target_os = "illumos",
604 target_os = "haiku",
605 target_os = "l4re",
606 target_os = "fuchsia",
607 target_os = "redox",
608 target_os = "vxworks",
609 target_os = "espidf"
610 ))]
ino(&self) -> u64611 pub fn ino(&self) -> u64 {
612 self.entry.d_ino as u64
613 }
614
615 #[cfg(any(
616 target_os = "freebsd",
617 target_os = "openbsd",
618 target_os = "netbsd",
619 target_os = "dragonfly"
620 ))]
ino(&self) -> u64621 pub fn ino(&self) -> u64 {
622 self.entry.d_fileno as u64
623 }
624
625 #[cfg(any(
626 target_os = "macos",
627 target_os = "ios",
628 target_os = "netbsd",
629 target_os = "openbsd",
630 target_os = "freebsd",
631 target_os = "dragonfly"
632 ))]
name_bytes(&self) -> &[u8]633 fn name_bytes(&self) -> &[u8] {
634 use crate::slice;
635 unsafe {
636 slice::from_raw_parts(
637 self.entry.d_name.as_ptr() as *const u8,
638 self.entry.d_namlen as usize,
639 )
640 }
641 }
642 #[cfg(any(
643 target_os = "android",
644 target_os = "linux",
645 target_os = "emscripten",
646 target_os = "l4re",
647 target_os = "haiku",
648 target_os = "vxworks",
649 target_os = "espidf"
650 ))]
name_bytes(&self) -> &[u8]651 fn name_bytes(&self) -> &[u8] {
652 unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() }
653 }
654 #[cfg(any(
655 target_os = "solaris",
656 target_os = "illumos",
657 target_os = "fuchsia",
658 target_os = "redox"
659 ))]
name_bytes(&self) -> &[u8]660 fn name_bytes(&self) -> &[u8] {
661 self.name.as_bytes()
662 }
663
664 #[cfg(not(any(
665 target_os = "solaris",
666 target_os = "illumos",
667 target_os = "fuchsia",
668 target_os = "redox"
669 )))]
name_cstr(&self) -> &CStr670 fn name_cstr(&self) -> &CStr {
671 unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
672 }
673 #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "fuchsia"))]
name_cstr(&self) -> &CStr674 fn name_cstr(&self) -> &CStr {
675 &self.name
676 }
677
file_name_os_str(&self) -> &OsStr678 pub fn file_name_os_str(&self) -> &OsStr {
679 OsStr::from_bytes(self.name_bytes())
680 }
681 }
682
683 impl OpenOptions {
new() -> OpenOptions684 pub fn new() -> OpenOptions {
685 OpenOptions {
686 // generic
687 read: false,
688 write: false,
689 append: false,
690 truncate: false,
691 create: false,
692 create_new: false,
693 // system-specific
694 custom_flags: 0,
695 mode: 0o666,
696 }
697 }
698
read(&mut self, read: bool)699 pub fn read(&mut self, read: bool) {
700 self.read = read;
701 }
write(&mut self, write: bool)702 pub fn write(&mut self, write: bool) {
703 self.write = write;
704 }
append(&mut self, append: bool)705 pub fn append(&mut self, append: bool) {
706 self.append = append;
707 }
truncate(&mut self, truncate: bool)708 pub fn truncate(&mut self, truncate: bool) {
709 self.truncate = truncate;
710 }
create(&mut self, create: bool)711 pub fn create(&mut self, create: bool) {
712 self.create = create;
713 }
create_new(&mut self, create_new: bool)714 pub fn create_new(&mut self, create_new: bool) {
715 self.create_new = create_new;
716 }
717
custom_flags(&mut self, flags: i32)718 pub fn custom_flags(&mut self, flags: i32) {
719 self.custom_flags = flags;
720 }
mode(&mut self, mode: u32)721 pub fn mode(&mut self, mode: u32) {
722 self.mode = mode as mode_t;
723 }
724
get_access_mode(&self) -> io::Result<c_int>725 fn get_access_mode(&self) -> io::Result<c_int> {
726 match (self.read, self.write, self.append) {
727 (true, false, false) => Ok(libc::O_RDONLY),
728 (false, true, false) => Ok(libc::O_WRONLY),
729 (true, true, false) => Ok(libc::O_RDWR),
730 (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
731 (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
732 (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
733 }
734 }
735
get_creation_mode(&self) -> io::Result<c_int>736 fn get_creation_mode(&self) -> io::Result<c_int> {
737 match (self.write, self.append) {
738 (true, false) => {}
739 (false, false) => {
740 if self.truncate || self.create || self.create_new {
741 return Err(Error::from_raw_os_error(libc::EINVAL));
742 }
743 }
744 (_, true) => {
745 if self.truncate && !self.create_new {
746 return Err(Error::from_raw_os_error(libc::EINVAL));
747 }
748 }
749 }
750
751 Ok(match (self.create, self.truncate, self.create_new) {
752 (false, false, false) => 0,
753 (true, false, false) => libc::O_CREAT,
754 (false, true, false) => libc::O_TRUNC,
755 (true, true, false) => libc::O_CREAT | libc::O_TRUNC,
756 (_, _, true) => libc::O_CREAT | libc::O_EXCL,
757 })
758 }
759 }
760
761 impl File {
open(path: &Path, opts: &OpenOptions) -> io::Result<File>762 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
763 let path = cstr(path)?;
764 File::open_c(&path, opts)
765 }
766
open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File>767 pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
768 let flags = libc::O_CLOEXEC
769 | opts.get_access_mode()?
770 | opts.get_creation_mode()?
771 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
772 // The third argument of `open64` is documented to have type `mode_t`. On
773 // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
774 // However, since this is a variadic function, C integer promotion rules mean that on
775 // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
776 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
777 Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
778 }
779
file_attr(&self) -> io::Result<FileAttr>780 pub fn file_attr(&self) -> io::Result<FileAttr> {
781 let fd = self.as_raw_fd();
782
783 cfg_has_statx! {
784 if let Some(ret) = unsafe { try_statx(
785 fd,
786 b"\0" as *const _ as *const c_char,
787 libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
788 libc::STATX_ALL,
789 ) } {
790 return ret;
791 }
792 }
793
794 let mut stat: stat64 = unsafe { mem::zeroed() };
795 cvt(unsafe { fstat64(fd, &mut stat) })?;
796 Ok(FileAttr::from_stat64(stat))
797 }
798
fsync(&self) -> io::Result<()>799 pub fn fsync(&self) -> io::Result<()> {
800 cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
801 return Ok(());
802
803 #[cfg(any(target_os = "macos", target_os = "ios"))]
804 unsafe fn os_fsync(fd: c_int) -> c_int {
805 libc::fcntl(fd, libc::F_FULLFSYNC)
806 }
807 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
808 unsafe fn os_fsync(fd: c_int) -> c_int {
809 libc::fsync(fd)
810 }
811 }
812
datasync(&self) -> io::Result<()>813 pub fn datasync(&self) -> io::Result<()> {
814 cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
815 return Ok(());
816
817 #[cfg(any(target_os = "macos", target_os = "ios"))]
818 unsafe fn os_datasync(fd: c_int) -> c_int {
819 libc::fcntl(fd, libc::F_FULLFSYNC)
820 }
821 #[cfg(any(
822 target_os = "freebsd",
823 target_os = "linux",
824 target_os = "android",
825 target_os = "netbsd",
826 target_os = "openbsd"
827 ))]
828 unsafe fn os_datasync(fd: c_int) -> c_int {
829 libc::fdatasync(fd)
830 }
831 #[cfg(not(any(
832 target_os = "android",
833 target_os = "freebsd",
834 target_os = "ios",
835 target_os = "linux",
836 target_os = "macos",
837 target_os = "netbsd",
838 target_os = "openbsd"
839 )))]
840 unsafe fn os_datasync(fd: c_int) -> c_int {
841 libc::fsync(fd)
842 }
843 }
844
truncate(&self, size: u64) -> io::Result<()>845 pub fn truncate(&self, size: u64) -> io::Result<()> {
846 #[cfg(target_os = "android")]
847 return crate::sys::android::ftruncate64(self.as_raw_fd(), size);
848
849 #[cfg(not(target_os = "android"))]
850 {
851 use crate::convert::TryInto;
852 let size: off64_t =
853 size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
854 cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
855 }
856 }
857
read(&self, buf: &mut [u8]) -> io::Result<usize>858 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
859 self.0.read(buf)
860 }
861
read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize>862 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
863 self.0.read_vectored(bufs)
864 }
865
866 #[inline]
is_read_vectored(&self) -> bool867 pub fn is_read_vectored(&self) -> bool {
868 self.0.is_read_vectored()
869 }
870
read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>871 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
872 self.0.read_at(buf, offset)
873 }
874
write(&self, buf: &[u8]) -> io::Result<usize>875 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
876 self.0.write(buf)
877 }
878
write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize>879 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
880 self.0.write_vectored(bufs)
881 }
882
883 #[inline]
is_write_vectored(&self) -> bool884 pub fn is_write_vectored(&self) -> bool {
885 self.0.is_write_vectored()
886 }
887
write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>888 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
889 self.0.write_at(buf, offset)
890 }
891
flush(&self) -> io::Result<()>892 pub fn flush(&self) -> io::Result<()> {
893 Ok(())
894 }
895
seek(&self, pos: SeekFrom) -> io::Result<u64>896 pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
897 let (whence, pos) = match pos {
898 // Casting to `i64` is fine, too large values will end up as
899 // negative which will cause an error in `lseek64`.
900 SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
901 SeekFrom::End(off) => (libc::SEEK_END, off),
902 SeekFrom::Current(off) => (libc::SEEK_CUR, off),
903 };
904 let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?;
905 Ok(n as u64)
906 }
907
duplicate(&self) -> io::Result<File>908 pub fn duplicate(&self) -> io::Result<File> {
909 self.0.duplicate().map(File)
910 }
911
set_permissions(&self, perm: FilePermissions) -> io::Result<()>912 pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
913 cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
914 Ok(())
915 }
916 }
917
918 impl DirBuilder {
new() -> DirBuilder919 pub fn new() -> DirBuilder {
920 DirBuilder { mode: 0o777 }
921 }
922
mkdir(&self, p: &Path) -> io::Result<()>923 pub fn mkdir(&self, p: &Path) -> io::Result<()> {
924 let p = cstr(p)?;
925 cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
926 Ok(())
927 }
928
set_mode(&mut self, mode: u32)929 pub fn set_mode(&mut self, mode: u32) {
930 self.mode = mode as mode_t;
931 }
932 }
933
cstr(path: &Path) -> io::Result<CString>934 fn cstr(path: &Path) -> io::Result<CString> {
935 Ok(CString::new(path.as_os_str().as_bytes())?)
936 }
937
938 impl AsInner<FileDesc> for File {
as_inner(&self) -> &FileDesc939 fn as_inner(&self) -> &FileDesc {
940 &self.0
941 }
942 }
943
944 impl AsInnerMut<FileDesc> for File {
as_inner_mut(&mut self) -> &mut FileDesc945 fn as_inner_mut(&mut self) -> &mut FileDesc {
946 &mut self.0
947 }
948 }
949
950 impl IntoInner<FileDesc> for File {
into_inner(self) -> FileDesc951 fn into_inner(self) -> FileDesc {
952 self.0
953 }
954 }
955
956 impl FromInner<FileDesc> for File {
from_inner(file_desc: FileDesc) -> Self957 fn from_inner(file_desc: FileDesc) -> Self {
958 Self(file_desc)
959 }
960 }
961
962 impl AsFd for File {
as_fd(&self) -> BorrowedFd<'_>963 fn as_fd(&self) -> BorrowedFd<'_> {
964 self.0.as_fd()
965 }
966 }
967
968 impl AsRawFd for File {
as_raw_fd(&self) -> RawFd969 fn as_raw_fd(&self) -> RawFd {
970 self.0.as_raw_fd()
971 }
972 }
973
974 impl IntoRawFd for File {
into_raw_fd(self) -> RawFd975 fn into_raw_fd(self) -> RawFd {
976 self.0.into_raw_fd()
977 }
978 }
979
980 impl FromRawFd for File {
from_raw_fd(raw_fd: RawFd) -> Self981 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
982 Self(FromRawFd::from_raw_fd(raw_fd))
983 }
984 }
985
986 impl fmt::Debug for File {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result987 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
988 #[cfg(any(target_os = "linux", target_os = "netbsd"))]
989 fn get_path(fd: c_int) -> Option<PathBuf> {
990 let mut p = PathBuf::from("/proc/self/fd");
991 p.push(&fd.to_string());
992 readlink(&p).ok()
993 }
994
995 #[cfg(target_os = "macos")]
996 fn get_path(fd: c_int) -> Option<PathBuf> {
997 // FIXME: The use of PATH_MAX is generally not encouraged, but it
998 // is inevitable in this case because macOS defines `fcntl` with
999 // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
1000 // alternatives. If a better method is invented, it should be used
1001 // instead.
1002 let mut buf = vec![0; libc::PATH_MAX as usize];
1003 let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
1004 if n == -1 {
1005 return None;
1006 }
1007 let l = buf.iter().position(|&c| c == 0).unwrap();
1008 buf.truncate(l as usize);
1009 buf.shrink_to_fit();
1010 Some(PathBuf::from(OsString::from_vec(buf)))
1011 }
1012
1013 #[cfg(target_os = "vxworks")]
1014 fn get_path(fd: c_int) -> Option<PathBuf> {
1015 let mut buf = vec![0; libc::PATH_MAX as usize];
1016 let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
1017 if n == -1 {
1018 return None;
1019 }
1020 let l = buf.iter().position(|&c| c == 0).unwrap();
1021 buf.truncate(l as usize);
1022 Some(PathBuf::from(OsString::from_vec(buf)))
1023 }
1024
1025 #[cfg(not(any(
1026 target_os = "linux",
1027 target_os = "macos",
1028 target_os = "vxworks",
1029 target_os = "netbsd"
1030 )))]
1031 fn get_path(_fd: c_int) -> Option<PathBuf> {
1032 // FIXME(#24570): implement this for other Unix platforms
1033 None
1034 }
1035
1036 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "vxworks"))]
1037 fn get_mode(fd: c_int) -> Option<(bool, bool)> {
1038 let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
1039 if mode == -1 {
1040 return None;
1041 }
1042 match mode & libc::O_ACCMODE {
1043 libc::O_RDONLY => Some((true, false)),
1044 libc::O_RDWR => Some((true, true)),
1045 libc::O_WRONLY => Some((false, true)),
1046 _ => None,
1047 }
1048 }
1049
1050 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))]
1051 fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
1052 // FIXME(#24570): implement this for other Unix platforms
1053 None
1054 }
1055
1056 let fd = self.as_raw_fd();
1057 let mut b = f.debug_struct("File");
1058 b.field("fd", &fd);
1059 if let Some(path) = get_path(fd) {
1060 b.field("path", &path);
1061 }
1062 if let Some((read, write)) = get_mode(fd) {
1063 b.field("read", &read).field("write", &write);
1064 }
1065 b.finish()
1066 }
1067 }
1068
readdir(p: &Path) -> io::Result<ReadDir>1069 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
1070 let root = p.to_path_buf();
1071 let p = cstr(p)?;
1072 unsafe {
1073 let ptr = libc::opendir(p.as_ptr());
1074 if ptr.is_null() {
1075 Err(Error::last_os_error())
1076 } else {
1077 let inner = InnerReadDir { dirp: Dir(ptr), root };
1078 Ok(ReadDir {
1079 inner: Arc::new(inner),
1080 #[cfg(not(any(
1081 target_os = "solaris",
1082 target_os = "illumos",
1083 target_os = "fuchsia",
1084 target_os = "redox",
1085 )))]
1086 end_of_stream: false,
1087 })
1088 }
1089 }
1090 }
1091
unlink(p: &Path) -> io::Result<()>1092 pub fn unlink(p: &Path) -> io::Result<()> {
1093 let p = cstr(p)?;
1094 cvt(unsafe { libc::unlink(p.as_ptr()) })?;
1095 Ok(())
1096 }
1097
rename(old: &Path, new: &Path) -> io::Result<()>1098 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
1099 let old = cstr(old)?;
1100 let new = cstr(new)?;
1101 cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
1102 Ok(())
1103 }
1104
set_perm(p: &Path, perm: FilePermissions) -> io::Result<()>1105 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
1106 let p = cstr(p)?;
1107 cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
1108 Ok(())
1109 }
1110
rmdir(p: &Path) -> io::Result<()>1111 pub fn rmdir(p: &Path) -> io::Result<()> {
1112 let p = cstr(p)?;
1113 cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
1114 Ok(())
1115 }
1116
readlink(p: &Path) -> io::Result<PathBuf>1117 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
1118 let c_path = cstr(p)?;
1119 let p = c_path.as_ptr();
1120
1121 let mut buf = Vec::with_capacity(256);
1122
1123 loop {
1124 let buf_read =
1125 cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
1126
1127 unsafe {
1128 buf.set_len(buf_read);
1129 }
1130
1131 if buf_read != buf.capacity() {
1132 buf.shrink_to_fit();
1133
1134 return Ok(PathBuf::from(OsString::from_vec(buf)));
1135 }
1136
1137 // Trigger the internal buffer resizing logic of `Vec` by requiring
1138 // more space than the current capacity. The length is guaranteed to be
1139 // the same as the capacity due to the if statement above.
1140 buf.reserve(1);
1141 }
1142 }
1143
symlink(original: &Path, link: &Path) -> io::Result<()>1144 pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
1145 let original = cstr(original)?;
1146 let link = cstr(link)?;
1147 cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?;
1148 Ok(())
1149 }
1150
link(original: &Path, link: &Path) -> io::Result<()>1151 pub fn link(original: &Path, link: &Path) -> io::Result<()> {
1152 let original = cstr(original)?;
1153 let link = cstr(link)?;
1154 cfg_if::cfg_if! {
1155 if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf"))] {
1156 // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
1157 // it implementation-defined whether `link` follows symlinks, so rely on the
1158 // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
1159 // Android has `linkat` on newer versions, but we happen to know `link`
1160 // always has the correct behavior, so it's here as well.
1161 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1162 } else if #[cfg(target_os = "macos")] {
1163 // On MacOS, older versions (<=10.9) lack support for linkat while newer
1164 // versions have it. We want to use linkat if it is available, so we use weak!
1165 // to check. `linkat` is preferable to `link` ecause it gives us a flag to
1166 // specify how symlinks should be handled. We pass 0 as the flags argument,
1167 // meaning it shouldn't follow symlinks.
1168 weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
1169
1170 if let Some(f) = linkat.get() {
1171 cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
1172 } else {
1173 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1174 };
1175 } else {
1176 // Where we can, use `linkat` instead of `link`; see the comment above
1177 // this one for details on why.
1178 cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
1179 }
1180 }
1181 Ok(())
1182 }
1183
stat(p: &Path) -> io::Result<FileAttr>1184 pub fn stat(p: &Path) -> io::Result<FileAttr> {
1185 let p = cstr(p)?;
1186
1187 cfg_has_statx! {
1188 if let Some(ret) = unsafe { try_statx(
1189 libc::AT_FDCWD,
1190 p.as_ptr(),
1191 libc::AT_STATX_SYNC_AS_STAT,
1192 libc::STATX_ALL,
1193 ) } {
1194 return ret;
1195 }
1196 }
1197
1198 let mut stat: stat64 = unsafe { mem::zeroed() };
1199 cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
1200 Ok(FileAttr::from_stat64(stat))
1201 }
1202
lstat(p: &Path) -> io::Result<FileAttr>1203 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
1204 let p = cstr(p)?;
1205
1206 cfg_has_statx! {
1207 if let Some(ret) = unsafe { try_statx(
1208 libc::AT_FDCWD,
1209 p.as_ptr(),
1210 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
1211 libc::STATX_ALL,
1212 ) } {
1213 return ret;
1214 }
1215 }
1216
1217 let mut stat: stat64 = unsafe { mem::zeroed() };
1218 cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
1219 Ok(FileAttr::from_stat64(stat))
1220 }
1221
canonicalize(p: &Path) -> io::Result<PathBuf>1222 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
1223 let path = CString::new(p.as_os_str().as_bytes())?;
1224 let buf;
1225 unsafe {
1226 let r = libc::realpath(path.as_ptr(), ptr::null_mut());
1227 if r.is_null() {
1228 return Err(io::Error::last_os_error());
1229 }
1230 buf = CStr::from_ptr(r).to_bytes().to_vec();
1231 libc::free(r as *mut _);
1232 }
1233 Ok(PathBuf::from(OsString::from_vec(buf)))
1234 }
1235
open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)>1236 fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1237 use crate::fs::File;
1238 use crate::sys_common::fs::NOT_FILE_ERROR;
1239
1240 let reader = File::open(from)?;
1241 let metadata = reader.metadata()?;
1242 if !metadata.is_file() {
1243 return Err(NOT_FILE_ERROR);
1244 }
1245 Ok((reader, metadata))
1246 }
1247
1248 #[cfg(target_os = "espidf")]
open_to_and_set_permissions( to: &Path, reader_metadata: crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)>1249 fn open_to_and_set_permissions(
1250 to: &Path,
1251 reader_metadata: crate::fs::Metadata,
1252 ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1253 use crate::fs::OpenOptions;
1254 let writer = OpenOptions::new().open(to)?;
1255 let writer_metadata = writer.metadata()?;
1256 Ok((writer, writer_metadata))
1257 }
1258
1259 #[cfg(not(target_os = "espidf"))]
open_to_and_set_permissions( to: &Path, reader_metadata: crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)>1260 fn open_to_and_set_permissions(
1261 to: &Path,
1262 reader_metadata: crate::fs::Metadata,
1263 ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1264 use crate::fs::OpenOptions;
1265 use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
1266
1267 let perm = reader_metadata.permissions();
1268 let writer = OpenOptions::new()
1269 // create the file with the correct mode right away
1270 .mode(perm.mode())
1271 .write(true)
1272 .create(true)
1273 .truncate(true)
1274 .open(to)?;
1275 let writer_metadata = writer.metadata()?;
1276 if writer_metadata.is_file() {
1277 // Set the correct file permissions, in case the file already existed.
1278 // Don't set the permissions on already existing non-files like
1279 // pipes/FIFOs or device nodes.
1280 writer.set_permissions(perm)?;
1281 }
1282 Ok((writer, writer_metadata))
1283 }
1284
1285 #[cfg(not(any(
1286 target_os = "linux",
1287 target_os = "android",
1288 target_os = "macos",
1289 target_os = "ios"
1290 )))]
copy(from: &Path, to: &Path) -> io::Result<u64>1291 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1292 let (mut reader, reader_metadata) = open_from(from)?;
1293 let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
1294
1295 io::copy(&mut reader, &mut writer)
1296 }
1297
1298 #[cfg(any(target_os = "linux", target_os = "android"))]
copy(from: &Path, to: &Path) -> io::Result<u64>1299 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1300 let (mut reader, reader_metadata) = open_from(from)?;
1301 let max_len = u64::MAX;
1302 let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
1303
1304 use super::kernel_copy::{copy_regular_files, CopyResult};
1305
1306 match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
1307 CopyResult::Ended(bytes) => Ok(bytes),
1308 CopyResult::Error(e, _) => Err(e),
1309 CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) {
1310 Ok(bytes) => Ok(bytes + written),
1311 Err(e) => Err(e),
1312 },
1313 }
1314 }
1315
1316 #[cfg(any(target_os = "macos", target_os = "ios"))]
copy(from: &Path, to: &Path) -> io::Result<u64>1317 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
1318 use crate::sync::atomic::{AtomicBool, Ordering};
1319
1320 const COPYFILE_ACL: u32 = 1 << 0;
1321 const COPYFILE_STAT: u32 = 1 << 1;
1322 const COPYFILE_XATTR: u32 = 1 << 2;
1323 const COPYFILE_DATA: u32 = 1 << 3;
1324
1325 const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL;
1326 const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR;
1327 const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA;
1328
1329 const COPYFILE_STATE_COPIED: u32 = 8;
1330
1331 #[allow(non_camel_case_types)]
1332 type copyfile_state_t = *mut libc::c_void;
1333 #[allow(non_camel_case_types)]
1334 type copyfile_flags_t = u32;
1335
1336 extern "C" {
1337 fn fcopyfile(
1338 from: libc::c_int,
1339 to: libc::c_int,
1340 state: copyfile_state_t,
1341 flags: copyfile_flags_t,
1342 ) -> libc::c_int;
1343 fn copyfile_state_alloc() -> copyfile_state_t;
1344 fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int;
1345 fn copyfile_state_get(
1346 state: copyfile_state_t,
1347 flag: u32,
1348 dst: *mut libc::c_void,
1349 ) -> libc::c_int;
1350 }
1351
1352 struct FreeOnDrop(copyfile_state_t);
1353 impl Drop for FreeOnDrop {
1354 fn drop(&mut self) {
1355 // The code below ensures that `FreeOnDrop` is never a null pointer
1356 unsafe {
1357 // `copyfile_state_free` returns -1 if the `to` or `from` files
1358 // cannot be closed. However, this is not considered this an
1359 // error.
1360 copyfile_state_free(self.0);
1361 }
1362 }
1363 }
1364
1365 // MacOS prior to 10.12 don't support `fclonefileat`
1366 // We store the availability in a global to avoid unnecessary syscalls
1367 static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true);
1368 syscall! {
1369 fn fclonefileat(
1370 srcfd: libc::c_int,
1371 dst_dirfd: libc::c_int,
1372 dst: *const c_char,
1373 flags: libc::c_int
1374 ) -> libc::c_int
1375 }
1376
1377 let (reader, reader_metadata) = open_from(from)?;
1378
1379 // Opportunistically attempt to create a copy-on-write clone of `from`
1380 // using `fclonefileat`.
1381 if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
1382 let to = cstr(to)?;
1383 let clonefile_result =
1384 cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) });
1385 match clonefile_result {
1386 Ok(_) => return Ok(reader_metadata.len()),
1387 Err(err) => match err.raw_os_error() {
1388 // `fclonefileat` will fail on non-APFS volumes, if the
1389 // destination already exists, or if the source and destination
1390 // are on different devices. In all these cases `fcopyfile`
1391 // should succeed.
1392 Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
1393 Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed),
1394 _ => return Err(err),
1395 },
1396 }
1397 }
1398
1399 // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
1400 let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?;
1401
1402 // We ensure that `FreeOnDrop` never contains a null pointer so it is
1403 // always safe to call `copyfile_state_free`
1404 let state = unsafe {
1405 let state = copyfile_state_alloc();
1406 if state.is_null() {
1407 return Err(crate::io::Error::last_os_error());
1408 }
1409 FreeOnDrop(state)
1410 };
1411
1412 let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA };
1413
1414 cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
1415
1416 let mut bytes_copied: libc::off_t = 0;
1417 cvt(unsafe {
1418 copyfile_state_get(
1419 state.0,
1420 COPYFILE_STATE_COPIED,
1421 &mut bytes_copied as *mut libc::off_t as *mut libc::c_void,
1422 )
1423 })?;
1424 Ok(bytes_copied as u64)
1425 }
1426
chown(path: &Path, uid: u32, gid: u32) -> io::Result<()>1427 pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
1428 let path = cstr(path)?;
1429 cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
1430 Ok(())
1431 }
1432
fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()>1433 pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
1434 cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?;
1435 Ok(())
1436 }
1437
lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()>1438 pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
1439 let path = cstr(path)?;
1440 cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?;
1441 Ok(())
1442 }
1443
1444 #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
chroot(dir: &Path) -> io::Result<()>1445 pub fn chroot(dir: &Path) -> io::Result<()> {
1446 let dir = cstr(dir)?;
1447 cvt(unsafe { libc::chroot(dir.as_ptr()) })?;
1448 Ok(())
1449 }
1450
1451 pub use remove_dir_impl::remove_dir_all;
1452
1453 // Fallback for REDOX
1454 #[cfg(target_os = "redox")]
1455 mod remove_dir_impl {
1456 pub use crate::sys_common::fs::remove_dir_all;
1457 }
1458
1459 // Dynamically choose implementation Macos x86-64: modern for 10.10+, fallback for older versions
1460 #[cfg(all(target_os = "macos", target_arch = "x86_64"))]
1461 mod remove_dir_impl {
1462 use super::{cstr, lstat, Dir, InnerReadDir, ReadDir};
1463 use crate::ffi::CStr;
1464 use crate::io;
1465 use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
1466 use crate::os::unix::prelude::{OwnedFd, RawFd};
1467 use crate::path::{Path, PathBuf};
1468 use crate::sync::Arc;
1469 use crate::sys::weak::weak;
1470 use crate::sys::{cvt, cvt_r};
1471 use libc::{c_char, c_int, DIR};
1472
openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd>1473 pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
1474 weak!(fn openat(c_int, *const c_char, c_int) -> c_int);
1475 let fd = cvt_r(|| unsafe {
1476 openat.get().unwrap()(
1477 parent_fd.unwrap_or(libc::AT_FDCWD),
1478 p.as_ptr(),
1479 libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
1480 )
1481 })?;
1482 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
1483 }
1484
fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)>1485 fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
1486 weak!(fn fdopendir(c_int) -> *mut DIR, "fdopendir$INODE64");
1487 let ptr = unsafe { fdopendir.get().unwrap()(dir_fd.as_raw_fd()) };
1488 if ptr.is_null() {
1489 return Err(io::Error::last_os_error());
1490 }
1491 let dirp = Dir(ptr);
1492 // file descriptor is automatically closed by libc::closedir() now, so give up ownership
1493 let new_parent_fd = dir_fd.into_raw_fd();
1494 // a valid root is not needed because we do not call any functions involving the full path
1495 // of the DirEntrys.
1496 let dummy_root = PathBuf::new();
1497 Ok((
1498 ReadDir {
1499 inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
1500 end_of_stream: false,
1501 },
1502 new_parent_fd,
1503 ))
1504 }
1505
remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()>1506 fn remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()> {
1507 weak!(fn unlinkat(c_int, *const c_char, c_int) -> c_int);
1508
1509 let pcstr = cstr(p)?;
1510
1511 // entry is expected to be a directory, open as such
1512 let fd = openat_nofollow_dironly(parent_fd, &pcstr)?;
1513
1514 // open the directory passing ownership of the fd
1515 let (dir, fd) = fdreaddir(fd)?;
1516 for child in dir {
1517 let child = child?;
1518 match child.entry.d_type {
1519 libc::DT_DIR => {
1520 remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1521 }
1522 libc::DT_UNKNOWN => {
1523 match cvt(unsafe { unlinkat.get().unwrap()(fd, child.name_cstr().as_ptr(), 0) })
1524 {
1525 // type unknown - try to unlink
1526 Err(err) if err.raw_os_error() == Some(libc::EPERM) => {
1527 // if the file is a directory unlink fails with EPERM
1528 remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1529 }
1530 result => {
1531 result?;
1532 }
1533 }
1534 }
1535 _ => {
1536 // not a directory -> unlink
1537 cvt(unsafe { unlinkat.get().unwrap()(fd, child.name_cstr().as_ptr(), 0) })?;
1538 }
1539 }
1540 }
1541
1542 // unlink the directory after removing its contents
1543 cvt(unsafe {
1544 unlinkat.get().unwrap()(
1545 parent_fd.unwrap_or(libc::AT_FDCWD),
1546 pcstr.as_ptr(),
1547 libc::AT_REMOVEDIR,
1548 )
1549 })?;
1550 Ok(())
1551 }
1552
remove_dir_all_modern(p: &Path) -> io::Result<()>1553 fn remove_dir_all_modern(p: &Path) -> io::Result<()> {
1554 // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
1555 // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
1556 // into symlinks.
1557 let attr = lstat(p)?;
1558 if attr.file_type().is_symlink() {
1559 crate::fs::remove_file(p)
1560 } else {
1561 remove_dir_all_recursive(None, p)
1562 }
1563 }
1564
remove_dir_all(p: &Path) -> io::Result<()>1565 pub fn remove_dir_all(p: &Path) -> io::Result<()> {
1566 weak!(fn openat(c_int, *const c_char, c_int) -> c_int);
1567 if openat.get().is_some() {
1568 // openat() is available with macOS 10.10+, just like unlinkat() and fdopendir()
1569 remove_dir_all_modern(p)
1570 } else {
1571 // fall back to classic implementation
1572 crate::sys_common::fs::remove_dir_all(p)
1573 }
1574 }
1575 }
1576
1577 // Modern implementation using openat(), unlinkat() and fdopendir()
1578 #[cfg(not(any(all(target_os = "macos", target_arch = "x86_64"), target_os = "redox")))]
1579 mod remove_dir_impl {
1580 use super::{cstr, lstat, Dir, DirEntry, InnerReadDir, ReadDir};
1581 use crate::ffi::CStr;
1582 use crate::io;
1583 use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
1584 use crate::os::unix::prelude::{OwnedFd, RawFd};
1585 use crate::path::{Path, PathBuf};
1586 use crate::sync::Arc;
1587 use crate::sys::{cvt, cvt_r};
1588 use libc::{fdopendir, openat, unlinkat};
1589
openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd>1590 pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
1591 let fd = cvt_r(|| unsafe {
1592 openat(
1593 parent_fd.unwrap_or(libc::AT_FDCWD),
1594 p.as_ptr(),
1595 libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
1596 )
1597 })?;
1598 Ok(unsafe { OwnedFd::from_raw_fd(fd) })
1599 }
1600
fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)>1601 fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
1602 let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) };
1603 if ptr.is_null() {
1604 return Err(io::Error::last_os_error());
1605 }
1606 let dirp = Dir(ptr);
1607 // file descriptor is automatically closed by libc::closedir() now, so give up ownership
1608 let new_parent_fd = dir_fd.into_raw_fd();
1609 // a valid root is not needed because we do not call any functions involving the full path
1610 // of the DirEntrys.
1611 let dummy_root = PathBuf::new();
1612 Ok((
1613 ReadDir {
1614 inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
1615 #[cfg(not(any(
1616 target_os = "solaris",
1617 target_os = "illumos",
1618 target_os = "fuchsia",
1619 target_os = "redox",
1620 )))]
1621 end_of_stream: false,
1622 },
1623 new_parent_fd,
1624 ))
1625 }
1626
1627 #[cfg(any(
1628 target_os = "solaris",
1629 target_os = "illumos",
1630 target_os = "haiku",
1631 target_os = "vxworks",
1632 target_os = "fuchsia"
1633 ))]
is_dir(_ent: &DirEntry) -> Option<bool>1634 fn is_dir(_ent: &DirEntry) -> Option<bool> {
1635 None
1636 }
1637
1638 #[cfg(not(any(
1639 target_os = "solaris",
1640 target_os = "illumos",
1641 target_os = "haiku",
1642 target_os = "vxworks",
1643 target_os = "fuchsia"
1644 )))]
is_dir(ent: &DirEntry) -> Option<bool>1645 fn is_dir(ent: &DirEntry) -> Option<bool> {
1646 match ent.entry.d_type {
1647 libc::DT_UNKNOWN => None,
1648 libc::DT_DIR => Some(true),
1649 _ => Some(false),
1650 }
1651 }
1652
remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()>1653 fn remove_dir_all_recursive(parent_fd: Option<RawFd>, p: &Path) -> io::Result<()> {
1654 let pcstr = cstr(p)?;
1655
1656 // entry is expected to be a directory, open as such
1657 let fd = openat_nofollow_dironly(parent_fd, &pcstr)?;
1658
1659 // open the directory passing ownership of the fd
1660 let (dir, fd) = fdreaddir(fd)?;
1661 for child in dir {
1662 let child = child?;
1663 match is_dir(&child) {
1664 Some(true) => {
1665 remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1666 }
1667 Some(false) => {
1668 cvt(unsafe { unlinkat(fd, child.name_cstr().as_ptr(), 0) })?;
1669 }
1670 None => match cvt(unsafe { unlinkat(fd, child.name_cstr().as_ptr(), 0) }) {
1671 // type unknown - try to unlink
1672 Err(err)
1673 if err.raw_os_error() == Some(libc::EISDIR)
1674 || err.raw_os_error() == Some(libc::EPERM) =>
1675 {
1676 // if the file is a directory unlink fails with EISDIR on Linux and EPERM everyhwere else
1677 remove_dir_all_recursive(Some(fd), Path::new(&child.file_name()))?;
1678 }
1679 result => {
1680 result?;
1681 }
1682 },
1683 }
1684 }
1685
1686 // unlink the directory after removing its contents
1687 cvt(unsafe {
1688 unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), pcstr.as_ptr(), libc::AT_REMOVEDIR)
1689 })?;
1690 Ok(())
1691 }
1692
remove_dir_all(p: &Path) -> io::Result<()>1693 pub fn remove_dir_all(p: &Path) -> io::Result<()> {
1694 // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
1695 // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
1696 // into symlinks.
1697 let attr = lstat(p)?;
1698 if attr.file_type().is_symlink() {
1699 crate::fs::remove_file(p)
1700 } else {
1701 remove_dir_all_recursive(None, p)
1702 }
1703 }
1704 }
1705