]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys/unix/fs.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.git] / library / std / src / sys / unix / fs.rs
CommitLineData
532ac7d7
XL
1use crate::os::unix::prelude::*;
2
dfeec247 3use crate::ffi::{CStr, CString, OsStr, OsString};
532ac7d7 4use crate::fmt;
cdc7bbd5 5use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom};
532ac7d7 6use crate::mem;
94222f64 7use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
532ac7d7
XL
8use crate::path::{Path, PathBuf};
9use crate::ptr;
10use crate::sync::Arc;
11use crate::sys::fd::FileDesc;
12use crate::sys::time::SystemTime;
13use crate::sys::{cvt, cvt_r};
94222f64 14use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
532ac7d7 15
136023e0
XL
16#[cfg(any(
17 all(target_os = "linux", target_env = "gnu"),
18 target_os = "macos",
19 target_os = "ios",
20))]
21use crate::sys::weak::syscall;
22#[cfg(target_os = "macos")]
23use crate::sys::weak::weak;
24
532ac7d7 25use libc::{c_int, mode_t};
7453a54e 26
136023e0
XL
27#[cfg(any(
28 target_os = "macos",
29 target_os = "ios",
30 all(target_os = "linux", target_env = "gnu")
31))]
32use libc::c_char;
94b46f34
XL
33#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
34use libc::dirfd;
dfeec247
XL
35#[cfg(any(target_os = "linux", target_os = "emscripten"))]
36use libc::fstatat64;
37#[cfg(not(any(
38 target_os = "linux",
39 target_os = "emscripten",
40 target_os = "solaris",
ba9703b0 41 target_os = "illumos",
dfeec247
XL
42 target_os = "l4re",
43 target_os = "fuchsia",
44 target_os = "redox"
45)))]
46use libc::readdir_r as readdir64_r;
7453a54e 47#[cfg(target_os = "android")]
dfeec247
XL
48use 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)))]
58use 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"))]
63use libc::{
64 dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64,
65};
85aaf69f 66
17df50a5 67pub use crate::sys_common::fs::{remove_dir_all, try_exists};
532ac7d7 68
85aaf69f
SL
69pub struct File(FileDesc);
70
dfeec247
XL
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
e74abb32
XL
75macro_rules! cfg_has_statx {
76 ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
77 cfg_if::cfg_if! {
dfeec247 78 if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
e74abb32
XL
79 $($then_tt)*
80 } else {
81 $($else_tt)*
82 }
83 }
84 };
85 ($($block_inner:tt)*) => {
dfeec247 86 #[cfg(all(target_os = "linux", target_env = "gnu"))]
e74abb32
XL
87 {
88 $($block_inner)*
89 }
90 };
85aaf69f
SL
91}
92
e74abb32
XL
93cfg_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,
136023e0 111 path: *const c_char,
e74abb32
XL
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,
136023e0 126 pathname: *const c_char,
e74abb32
XL
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 => {
17df50a5 135 // It is a trick to call `statx` with null pointers to check if the syscall
e74abb32
XL
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
60c5eb7d 138 // faster than a normal successful call.
e74abb32
XL
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
94b46f34
XL
196// all DirEntry's will have a reference to this struct
197struct InnerReadDir {
c34b1796 198 dirp: Dir,
94b46f34 199 root: PathBuf,
85aaf69f
SL
200}
201
8faf50e0
XL
202pub struct ReadDir {
203 inner: Arc<InnerReadDir>,
29967ef6
XL
204 #[cfg(not(any(
205 target_os = "solaris",
206 target_os = "illumos",
207 target_os = "fuchsia",
208 target_os = "redox",
209 )))]
8faf50e0
XL
210 end_of_stream: bool,
211}
94b46f34 212
c34b1796
AL
213struct Dir(*mut libc::DIR);
214
215unsafe impl Send for Dir {}
216unsafe impl Sync for Dir {}
217
85aaf69f 218pub struct DirEntry {
7453a54e 219 entry: dirent64,
29967ef6 220 dir: Arc<InnerReadDir>,
94b46f34 221 // We need to store an owned copy of the entry name
8bb4bdeb
XL
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.
ba9703b0
XL
225 #[cfg(any(
226 target_os = "solaris",
227 target_os = "illumos",
228 target_os = "fuchsia",
229 target_os = "redox"
230 ))]
dfeec247 231 name: Box<[u8]>,
85aaf69f
SL
232}
233
32a655c1 234#[derive(Clone, Debug)]
85aaf69f 235pub struct OpenOptions {
7453a54e 236 // generic
85aaf69f
SL
237 read: bool,
238 write: bool,
7453a54e
SL
239 append: bool,
240 truncate: bool,
241 create: bool,
242 create_new: bool,
243 // system-specific
244 custom_flags: i32,
85aaf69f
SL
245 mode: mode_t,
246}
247
248#[derive(Clone, PartialEq, Eq, Debug)]
dfeec247
XL
249pub struct FilePermissions {
250 mode: mode_t,
251}
85aaf69f 252
5bcae85e 253#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
dfeec247
XL
254pub struct FileType {
255 mode: mode_t,
256}
d9579d0f 257
32a655c1 258#[derive(Debug)]
dfeec247
XL
259pub struct DirBuilder {
260 mode: mode_t,
261}
d9579d0f 262
e74abb32
XL
263cfg_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
85aaf69f 277impl FileAttr {
dfeec247
XL
278 pub fn size(&self) -> u64 {
279 self.stat.st_size as u64
280 }
85aaf69f 281 pub fn perm(&self) -> FilePermissions {
ea8adc8c 282 FilePermissions { mode: (self.stat.st_mode as mode_t) }
85aaf69f
SL
283 }
284
d9579d0f
AL
285 pub fn file_type(&self) -> FileType {
286 FileType { mode: self.stat.st_mode as mode_t }
85aaf69f 287 }
d9579d0f 288}
85aaf69f 289
7453a54e
SL
290#[cfg(target_os = "netbsd")]
291impl FileAttr {
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
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
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 }
d9579d0f
AL
312}
313
a7813a04 314#[cfg(not(target_os = "netbsd"))]
7453a54e 315impl FileAttr {
94222f64 316 #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
7453a54e
SL
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,
abe05a73 320 tv_nsec: self.stat.st_mtime_nsec as _,
7453a54e
SL
321 }))
322 }
323
94222f64 324 #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
29967ef6
XL
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
94222f64 332 #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
7453a54e
SL
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,
abe05a73 336 tv_nsec: self.stat.st_atime_nsec as _,
7453a54e
SL
337 }))
338 }
339
94222f64 340 #[cfg(any(target_os = "vxworks", target_os = "espidf"))]
29967ef6
XL
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
dfeec247
XL
348 #[cfg(any(
349 target_os = "freebsd",
350 target_os = "openbsd",
351 target_os = "macos",
352 target_os = "ios"
353 ))]
7453a54e
SL
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
dfeec247
XL
361 #[cfg(not(any(
362 target_os = "freebsd",
363 target_os = "openbsd",
364 target_os = "macos",
365 target_os = "ios"
366 )))]
7453a54e 367 pub fn created(&self) -> io::Result<SystemTime> {
e74abb32
XL
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 {
cdc7bbd5 376 Err(io::Error::new_const(
136023e0 377 io::ErrorKind::Uncategorized,
cdc7bbd5 378 &"creation time is not available for the filesystem",
e74abb32
XL
379 ))
380 };
381 }
382 }
383
cdc7bbd5
XL
384 Err(io::Error::new_const(
385 io::ErrorKind::Unsupported,
386 &"creation time is not available on this platform \
dfeec247
XL
387 currently",
388 ))
7453a54e
SL
389 }
390}
391
392impl AsInner<stat64> for FileAttr {
dfeec247
XL
393 fn as_inner(&self) -> &stat64 {
394 &self.stat
395 }
85aaf69f
SL
396}
397
398impl FilePermissions {
3b2f2976
XL
399 pub fn readonly(&self) -> bool {
400 // check if any class (owner, group, others) has write permission
401 self.mode & 0o222 == 0
402 }
403
85aaf69f
SL
404 pub fn set_readonly(&mut self, readonly: bool) {
405 if readonly {
3b2f2976 406 // remove write permission for all classes; equivalent to `chmod a-w <file>`
85aaf69f
SL
407 self.mode &= !0o222;
408 } else {
3b2f2976 409 // add write permission for all classes; equivalent to `chmod a+w <file>`
85aaf69f
SL
410 self.mode |= 0o222;
411 }
412 }
dfeec247
XL
413 pub fn mode(&self) -> u32 {
414 self.mode as u32
415 }
d9579d0f
AL
416}
417
418impl FileType {
dfeec247
XL
419 pub fn is_dir(&self) -> bool {
420 self.is(libc::S_IFDIR)
421 }
422 pub fn is_file(&self) -> bool {
423 self.is(libc::S_IFREG)
424 }
425 pub fn is_symlink(&self) -> bool {
426 self.is(libc::S_IFLNK)
427 }
d9579d0f 428
dfeec247
XL
429 pub fn is(&self, mode: mode_t) -> bool {
430 self.mode & libc::S_IFMT == mode
431 }
85aaf69f
SL
432}
433
7453a54e
SL
434impl FromInner<u32> for FilePermissions {
435 fn from_inner(mode: u32) -> FilePermissions {
85aaf69f
SL
436 FilePermissions { mode: mode as mode_t }
437 }
438}
439
c30ab7b3 440impl fmt::Debug for ReadDir {
532ac7d7 441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
c30ab7b3
SL
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")'
8faf50e0 444 fmt::Debug::fmt(&*self.inner.root, f)
c30ab7b3
SL
445 }
446}
447
85aaf69f
SL
448impl Iterator for ReadDir {
449 type Item = io::Result<DirEntry>;
450
ba9703b0
XL
451 #[cfg(any(
452 target_os = "solaris",
453 target_os = "fuchsia",
454 target_os = "redox",
455 target_os = "illumos"
456 ))]
7453a54e 457 fn next(&mut self) -> Option<io::Result<DirEntry>> {
532ac7d7
XL
458 use crate::slice;
459
7453a54e
SL
460 unsafe {
461 loop {
462 // Although readdir_r(3) would be a correct function to use here because
8bb4bdeb
XL
463 // of the thread safety, on Illumos and Fuchsia the readdir(3C) function
464 // is safe to use in threaded applications and it is generally preferred
465 // over the readdir_r(3C) function.
5bcae85e 466 super::os::set_errno(0);
8faf50e0 467 let entry_ptr = libc::readdir(self.inner.dirp.0);
7453a54e 468 if entry_ptr.is_null() {
17df50a5 469 // null can mean either the end is reached or an error occurred.
5bcae85e
SL
470 // So we had to clear errno beforehand to check for an error now.
471 return match super::os::errno() {
472 0 => None,
473 e => Some(Err(Error::from_raw_os_error(e))),
dfeec247 474 };
7453a54e
SL
475 }
476
477 let name = (*entry_ptr).d_name.as_ptr();
478 let namelen = libc::strlen(name) as usize;
479
480 let ret = DirEntry {
481 entry: *entry_ptr,
dfeec247
XL
482 name: slice::from_raw_parts(name as *const u8, namelen as usize)
483 .to_owned()
484 .into_boxed_slice(),
29967ef6 485 dir: Arc::clone(&self.inner),
7453a54e
SL
486 };
487 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
dfeec247 488 return Some(Ok(ret));
7453a54e
SL
489 }
490 }
491 }
492 }
493
ba9703b0
XL
494 #[cfg(not(any(
495 target_os = "solaris",
496 target_os = "fuchsia",
497 target_os = "redox",
498 target_os = "illumos"
499 )))]
85aaf69f 500 fn next(&mut self) -> Option<io::Result<DirEntry>> {
8faf50e0
XL
501 if self.end_of_stream {
502 return None;
503 }
504
9cc50fc6 505 unsafe {
29967ef6 506 let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
9cc50fc6
SL
507 let mut entry_ptr = ptr::null_mut();
508 loop {
94222f64
XL
509 let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr);
510 if err != 0 {
8faf50e0
XL
511 if entry_ptr.is_null() {
512 // We encountered an error (which will be returned in this iteration), but
513 // we also reached the end of the directory stream. The `end_of_stream`
514 // flag is enabled to make sure that we return `None` in the next iteration
515 // (instead of looping forever)
516 self.end_of_stream = true;
517 }
94222f64 518 return Some(Err(Error::from_raw_os_error(err)));
9cc50fc6
SL
519 }
520 if entry_ptr.is_null() {
dfeec247 521 return None;
9cc50fc6
SL
522 }
523 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
dfeec247 524 return Some(Ok(ret));
9cc50fc6 525 }
85aaf69f
SL
526 }
527 }
528 }
529}
530
c34b1796 531impl Drop for Dir {
85aaf69f 532 fn drop(&mut self) {
c34b1796 533 let r = unsafe { libc::closedir(self.0) };
85aaf69f
SL
534 debug_assert_eq!(r, 0);
535 }
536}
537
538impl DirEntry {
539 pub fn path(&self) -> PathBuf {
29967ef6 540 self.dir.root.join(OsStr::from_bytes(self.name_bytes()))
85aaf69f
SL
541 }
542
d9579d0f
AL
543 pub fn file_name(&self) -> OsString {
544 OsStr::from_bytes(self.name_bytes()).to_os_string()
545 }
546
94b46f34
XL
547 #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
548 pub fn metadata(&self) -> io::Result<FileAttr> {
29967ef6 549 let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
e74abb32
XL
550 let name = self.entry.d_name.as_ptr();
551
552 cfg_has_statx! {
553 if let Some(ret) = unsafe { try_statx(
554 fd,
555 name,
556 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
557 libc::STATX_ALL,
558 ) } {
559 return ret;
560 }
561 }
562
94b46f34 563 let mut stat: stat64 = unsafe { mem::zeroed() };
dfeec247 564 cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
e74abb32 565 Ok(FileAttr::from_stat64(stat))
94b46f34
XL
566 }
567
568 #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
d9579d0f
AL
569 pub fn metadata(&self) -> io::Result<FileAttr> {
570 lstat(&self.path())
571 }
572
29967ef6
XL
573 #[cfg(any(
574 target_os = "solaris",
575 target_os = "illumos",
576 target_os = "haiku",
577 target_os = "vxworks"
578 ))]
9e0c209e
SL
579 pub fn file_type(&self) -> io::Result<FileType> {
580 lstat(&self.path()).map(|m| m.file_type())
581 }
582
29967ef6
XL
583 #[cfg(not(any(
584 target_os = "solaris",
585 target_os = "illumos",
586 target_os = "haiku",
587 target_os = "vxworks"
588 )))]
d9579d0f 589 pub fn file_type(&self) -> io::Result<FileType> {
9cc50fc6
SL
590 match self.entry.d_type {
591 libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
592 libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
593 libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
594 libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
595 libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
596 libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
597 libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
598 _ => lstat(&self.path()).map(|m| m.file_type()),
d9579d0f
AL
599 }
600 }
601
dfeec247
XL
602 #[cfg(any(
603 target_os = "macos",
604 target_os = "ios",
605 target_os = "linux",
606 target_os = "emscripten",
607 target_os = "android",
608 target_os = "solaris",
ba9703b0 609 target_os = "illumos",
dfeec247
XL
610 target_os = "haiku",
611 target_os = "l4re",
612 target_os = "fuchsia",
29967ef6 613 target_os = "redox",
94222f64
XL
614 target_os = "vxworks",
615 target_os = "espidf"
dfeec247 616 ))]
7453a54e
SL
617 pub fn ino(&self) -> u64 {
618 self.entry.d_ino as u64
9cc50fc6
SL
619 }
620
dfeec247
XL
621 #[cfg(any(
622 target_os = "freebsd",
623 target_os = "openbsd",
624 target_os = "netbsd",
625 target_os = "dragonfly"
626 ))]
7453a54e
SL
627 pub fn ino(&self) -> u64 {
628 self.entry.d_fileno as u64
d9579d0f
AL
629 }
630
dfeec247
XL
631 #[cfg(any(
632 target_os = "macos",
633 target_os = "ios",
634 target_os = "netbsd",
635 target_os = "openbsd",
636 target_os = "freebsd",
637 target_os = "dragonfly"
638 ))]
9cc50fc6 639 fn name_bytes(&self) -> &[u8] {
532ac7d7 640 use crate::slice;
85aaf69f 641 unsafe {
dfeec247
XL
642 slice::from_raw_parts(
643 self.entry.d_name.as_ptr() as *const u8,
644 self.entry.d_namlen as usize,
645 )
85aaf69f
SL
646 }
647 }
dfeec247
XL
648 #[cfg(any(
649 target_os = "android",
650 target_os = "linux",
651 target_os = "emscripten",
652 target_os = "l4re",
29967ef6 653 target_os = "haiku",
94222f64
XL
654 target_os = "vxworks",
655 target_os = "espidf"
dfeec247 656 ))]
9cc50fc6 657 fn name_bytes(&self) -> &[u8] {
dfeec247 658 unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() }
c34b1796 659 }
ba9703b0
XL
660 #[cfg(any(
661 target_os = "solaris",
662 target_os = "illumos",
663 target_os = "fuchsia",
664 target_os = "redox"
665 ))]
7453a54e
SL
666 fn name_bytes(&self) -> &[u8] {
667 &*self.name
668 }
136023e0
XL
669
670 pub fn file_name_os_str(&self) -> &OsStr {
671 OsStr::from_bytes(self.name_bytes())
672 }
85aaf69f
SL
673}
674
675impl OpenOptions {
676 pub fn new() -> OpenOptions {
677 OpenOptions {
7453a54e 678 // generic
85aaf69f
SL
679 read: false,
680 write: false,
7453a54e
SL
681 append: false,
682 truncate: false,
683 create: false,
684 create_new: false,
685 // system-specific
686 custom_flags: 0,
85aaf69f
SL
687 mode: 0o666,
688 }
689 }
690
dfeec247
XL
691 pub fn read(&mut self, read: bool) {
692 self.read = read;
693 }
694 pub fn write(&mut self, write: bool) {
695 self.write = write;
696 }
697 pub fn append(&mut self, append: bool) {
698 self.append = append;
699 }
700 pub fn truncate(&mut self, truncate: bool) {
701 self.truncate = truncate;
702 }
703 pub fn create(&mut self, create: bool) {
704 self.create = create;
705 }
706 pub fn create_new(&mut self, create_new: bool) {
707 self.create_new = create_new;
708 }
7453a54e 709
dfeec247
XL
710 pub fn custom_flags(&mut self, flags: i32) {
711 self.custom_flags = flags;
712 }
713 pub fn mode(&mut self, mode: u32) {
714 self.mode = mode as mode_t;
715 }
7453a54e
SL
716
717 fn get_access_mode(&self) -> io::Result<c_int> {
718 match (self.read, self.write, self.append) {
dfeec247
XL
719 (true, false, false) => Ok(libc::O_RDONLY),
720 (false, true, false) => Ok(libc::O_WRONLY),
721 (true, true, false) => Ok(libc::O_RDWR),
722 (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
723 (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
7453a54e
SL
724 (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
725 }
85aaf69f
SL
726 }
727
7453a54e
SL
728 fn get_creation_mode(&self) -> io::Result<c_int> {
729 match (self.write, self.append) {
730 (true, false) => {}
dfeec247 731 (false, false) => {
7453a54e
SL
732 if self.truncate || self.create || self.create_new {
733 return Err(Error::from_raw_os_error(libc::EINVAL));
dfeec247
XL
734 }
735 }
736 (_, true) => {
7453a54e
SL
737 if self.truncate && !self.create_new {
738 return Err(Error::from_raw_os_error(libc::EINVAL));
dfeec247
XL
739 }
740 }
85aaf69f 741 }
7453a54e
SL
742
743 Ok(match (self.create, self.truncate, self.create_new) {
dfeec247
XL
744 (false, false, false) => 0,
745 (true, false, false) => libc::O_CREAT,
746 (false, true, false) => libc::O_TRUNC,
747 (true, true, false) => libc::O_CREAT | libc::O_TRUNC,
748 (_, _, true) => libc::O_CREAT | libc::O_EXCL,
749 })
85aaf69f
SL
750 }
751}
752
753impl File {
754 pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
54a0048b 755 let path = cstr(path)?;
9346a6ac
AL
756 File::open_c(&path, opts)
757 }
758
759 pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
dfeec247
XL
760 let flags = libc::O_CLOEXEC
761 | opts.get_access_mode()?
762 | opts.get_creation_mode()?
763 | (opts.custom_flags as c_int & !libc::O_ACCMODE);
f9f354fc
XL
764 // The third argument of `open64` is documented to have type `mode_t`. On
765 // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
766 // However, since this is a variadic function, C integer promotion rules mean that on
767 // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
dfeec247 768 let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
94222f64 769 Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
85aaf69f
SL
770 }
771
772 pub fn file_attr(&self) -> io::Result<FileAttr> {
94222f64 773 let fd = self.as_raw_fd();
e74abb32
XL
774
775 cfg_has_statx! {
776 if let Some(ret) = unsafe { try_statx(
777 fd,
136023e0 778 b"\0" as *const _ as *const c_char,
e74abb32
XL
779 libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
780 libc::STATX_ALL,
781 ) } {
782 return ret;
783 }
784 }
785
7453a54e 786 let mut stat: stat64 = unsafe { mem::zeroed() };
dfeec247 787 cvt(unsafe { fstat64(fd, &mut stat) })?;
e74abb32 788 Ok(FileAttr::from_stat64(stat))
85aaf69f
SL
789 }
790
791 pub fn fsync(&self) -> io::Result<()> {
94222f64 792 cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
48663c56
XL
793 return Ok(());
794
795 #[cfg(any(target_os = "macos", target_os = "ios"))]
796 unsafe fn os_fsync(fd: c_int) -> c_int {
797 libc::fcntl(fd, libc::F_FULLFSYNC)
798 }
799 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
dfeec247
XL
800 unsafe fn os_fsync(fd: c_int) -> c_int {
801 libc::fsync(fd)
802 }
85aaf69f
SL
803 }
804
805 pub fn datasync(&self) -> io::Result<()> {
94222f64 806 cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
85aaf69f
SL
807 return Ok(());
808
809 #[cfg(any(target_os = "macos", target_os = "ios"))]
810 unsafe fn os_datasync(fd: c_int) -> c_int {
811 libc::fcntl(fd, libc::F_FULLFSYNC)
812 }
29967ef6
XL
813 #[cfg(any(
814 target_os = "freebsd",
815 target_os = "linux",
816 target_os = "android",
817 target_os = "netbsd",
818 target_os = "openbsd"
819 ))]
dfeec247
XL
820 unsafe fn os_datasync(fd: c_int) -> c_int {
821 libc::fdatasync(fd)
822 }
29967ef6
XL
823 #[cfg(not(any(
824 target_os = "android",
825 target_os = "freebsd",
826 target_os = "ios",
827 target_os = "linux",
828 target_os = "macos",
829 target_os = "netbsd",
830 target_os = "openbsd"
831 )))]
dfeec247
XL
832 unsafe fn os_datasync(fd: c_int) -> c_int {
833 libc::fsync(fd)
834 }
85aaf69f
SL
835 }
836
837 pub fn truncate(&self, size: u64) -> io::Result<()> {
a7813a04 838 #[cfg(target_os = "android")]
94222f64 839 return crate::sys::android::ftruncate64(self.as_raw_fd(), size);
a7813a04
XL
840
841 #[cfg(not(target_os = "android"))]
416331ca
XL
842 {
843 use crate::convert::TryInto;
dfeec247
XL
844 let size: off64_t =
845 size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
94222f64 846 cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
416331ca 847 }
85aaf69f
SL
848 }
849
850 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
851 self.0.read(buf)
852 }
853
48663c56
XL
854 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
855 self.0.read_vectored(bufs)
856 }
857
f9f354fc
XL
858 #[inline]
859 pub fn is_read_vectored(&self) -> bool {
860 self.0.is_read_vectored()
861 }
862
c30ab7b3
SL
863 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
864 self.0.read_at(buf, offset)
865 }
866
85aaf69f
SL
867 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
868 self.0.write(buf)
869 }
870
48663c56
XL
871 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
872 self.0.write_vectored(bufs)
873 }
874
f9f354fc
XL
875 #[inline]
876 pub fn is_write_vectored(&self) -> bool {
877 self.0.is_write_vectored()
878 }
879
c30ab7b3
SL
880 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
881 self.0.write_at(buf, offset)
882 }
883
dfeec247
XL
884 pub fn flush(&self) -> io::Result<()> {
885 Ok(())
886 }
85aaf69f
SL
887
888 pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
889 let (whence, pos) = match pos {
5bcae85e
SL
890 // Casting to `i64` is fine, too large values will end up as
891 // negative which will cause an error in `lseek64`.
892 SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
893 SeekFrom::End(off) => (libc::SEEK_END, off),
894 SeekFrom::Current(off) => (libc::SEEK_CUR, off),
85aaf69f 895 };
94222f64 896 let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?;
85aaf69f
SL
897 Ok(n as u64)
898 }
899
7453a54e
SL
900 pub fn duplicate(&self) -> io::Result<File> {
901 self.0.duplicate().map(File)
902 }
903
476ff2be 904 pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
94222f64 905 cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
476ff2be
SL
906 Ok(())
907 }
85aaf69f
SL
908}
909
d9579d0f
AL
910impl DirBuilder {
911 pub fn new() -> DirBuilder {
912 DirBuilder { mode: 0o777 }
913 }
914
915 pub fn mkdir(&self, p: &Path) -> io::Result<()> {
54a0048b
SL
916 let p = cstr(p)?;
917 cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
d9579d0f
AL
918 Ok(())
919 }
920
7453a54e
SL
921 pub fn set_mode(&mut self, mode: u32) {
922 self.mode = mode as mode_t;
d9579d0f
AL
923 }
924}
925
85aaf69f 926fn cstr(path: &Path) -> io::Result<CString> {
54a0048b 927 Ok(CString::new(path.as_os_str().as_bytes())?)
c34b1796
AL
928}
929
94222f64
XL
930impl AsInner<FileDesc> for File {
931 fn as_inner(&self) -> &FileDesc {
932 &self.0
933 }
934}
935
936impl AsInnerMut<FileDesc> for File {
937 fn as_inner_mut(&mut self) -> &mut FileDesc {
938 &mut self.0
939 }
940}
941
942impl IntoInner<FileDesc> for File {
943 fn into_inner(self) -> FileDesc {
944 self.0
945 }
946}
947
948impl FromInner<FileDesc> for File {
949 fn from_inner(file_desc: FileDesc) -> Self {
950 Self(file_desc)
951 }
952}
953
954impl AsFd for File {
955 fn as_fd(&self) -> BorrowedFd<'_> {
956 self.0.as_fd()
957 }
958}
959
960impl AsRawFd for File {
961 fn as_raw_fd(&self) -> RawFd {
962 self.0.as_raw_fd()
963 }
964}
965
966impl IntoRawFd for File {
967 fn into_raw_fd(self) -> RawFd {
968 self.0.into_raw_fd()
969 }
970}
971
972impl FromRawFd for File {
973 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
974 Self(FromRawFd::from_raw_fd(raw_fd))
c34b1796 975 }
85aaf69f
SL
976}
977
d9579d0f 978impl fmt::Debug for File {
532ac7d7 979 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94222f64 980 #[cfg(any(target_os = "linux", target_os = "netbsd"))]
d9579d0f 981 fn get_path(fd: c_int) -> Option<PathBuf> {
d9579d0f
AL
982 let mut p = PathBuf::from("/proc/self/fd");
983 p.push(&fd.to_string());
984 readlink(&p).ok()
985 }
986
c1a9b12d
SL
987 #[cfg(target_os = "macos")]
988 fn get_path(fd: c_int) -> Option<PathBuf> {
e9174d1e 989 // FIXME: The use of PATH_MAX is generally not encouraged, but it
cc61c64b 990 // is inevitable in this case because macOS defines `fcntl` with
e9174d1e
SL
991 // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
992 // alternatives. If a better method is invented, it should be used
993 // instead.
dfeec247 994 let mut buf = vec![0; libc::PATH_MAX as usize];
c1a9b12d
SL
995 let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
996 if n == -1 {
997 return None;
998 }
999 let l = buf.iter().position(|&c| c == 0).unwrap();
1000 buf.truncate(l as usize);
e9174d1e 1001 buf.shrink_to_fit();
c1a9b12d
SL
1002 Some(PathBuf::from(OsString::from_vec(buf)))
1003 }
1004
29967ef6
XL
1005 #[cfg(target_os = "vxworks")]
1006 fn get_path(fd: c_int) -> Option<PathBuf> {
1007 let mut buf = vec![0; libc::PATH_MAX as usize];
1008 let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
1009 if n == -1 {
1010 return None;
1011 }
1012 let l = buf.iter().position(|&c| c == 0).unwrap();
1013 buf.truncate(l as usize);
1014 Some(PathBuf::from(OsString::from_vec(buf)))
1015 }
1016
94222f64
XL
1017 #[cfg(not(any(
1018 target_os = "linux",
1019 target_os = "macos",
1020 target_os = "vxworks",
1021 target_os = "netbsd"
1022 )))]
d9579d0f
AL
1023 fn get_path(_fd: c_int) -> Option<PathBuf> {
1024 // FIXME(#24570): implement this for other Unix platforms
1025 None
1026 }
1027
29967ef6 1028 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "vxworks"))]
d9579d0f
AL
1029 fn get_mode(fd: c_int) -> Option<(bool, bool)> {
1030 let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
1031 if mode == -1 {
1032 return None;
1033 }
1034 match mode & libc::O_ACCMODE {
1035 libc::O_RDONLY => Some((true, false)),
1036 libc::O_RDWR => Some((true, true)),
1037 libc::O_WRONLY => Some((false, true)),
dfeec247 1038 _ => None,
d9579d0f
AL
1039 }
1040 }
1041
29967ef6 1042 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))]
d9579d0f
AL
1043 fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
1044 // FIXME(#24570): implement this for other Unix platforms
1045 None
1046 }
1047
94222f64 1048 let fd = self.as_raw_fd();
62682a34
SL
1049 let mut b = f.debug_struct("File");
1050 b.field("fd", &fd);
d9579d0f 1051 if let Some(path) = get_path(fd) {
62682a34 1052 b.field("path", &path);
d9579d0f
AL
1053 }
1054 if let Some((read, write)) = get_mode(fd) {
62682a34 1055 b.field("read", &read).field("write", &write);
d9579d0f
AL
1056 }
1057 b.finish()
1058 }
85aaf69f
SL
1059}
1060
1061pub fn readdir(p: &Path) -> io::Result<ReadDir> {
94b46f34 1062 let root = p.to_path_buf();
54a0048b 1063 let p = cstr(p)?;
85aaf69f
SL
1064 unsafe {
1065 let ptr = libc::opendir(p.as_ptr());
1066 if ptr.is_null() {
1067 Err(Error::last_os_error())
1068 } else {
94b46f34 1069 let inner = InnerReadDir { dirp: Dir(ptr), root };
29967ef6
XL
1070 Ok(ReadDir {
1071 inner: Arc::new(inner),
1072 #[cfg(not(any(
1073 target_os = "solaris",
1074 target_os = "illumos",
1075 target_os = "fuchsia",
1076 target_os = "redox",
1077 )))]
1078 end_of_stream: false,
1079 })
85aaf69f
SL
1080 }
1081 }
1082}
1083
1084pub fn unlink(p: &Path) -> io::Result<()> {
54a0048b
SL
1085 let p = cstr(p)?;
1086 cvt(unsafe { libc::unlink(p.as_ptr()) })?;
85aaf69f
SL
1087 Ok(())
1088}
1089
1090pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
54a0048b
SL
1091 let old = cstr(old)?;
1092 let new = cstr(new)?;
1093 cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
85aaf69f
SL
1094 Ok(())
1095}
1096
1097pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
54a0048b
SL
1098 let p = cstr(p)?;
1099 cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
85aaf69f
SL
1100 Ok(())
1101}
1102
1103pub fn rmdir(p: &Path) -> io::Result<()> {
54a0048b
SL
1104 let p = cstr(p)?;
1105 cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
85aaf69f
SL
1106 Ok(())
1107}
1108
85aaf69f 1109pub fn readlink(p: &Path) -> io::Result<PathBuf> {
54a0048b 1110 let c_path = cstr(p)?;
85aaf69f 1111 let p = c_path.as_ptr();
e9174d1e
SL
1112
1113 let mut buf = Vec::with_capacity(256);
1114
1115 loop {
dfeec247
XL
1116 let buf_read =
1117 cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
e9174d1e 1118
dfeec247
XL
1119 unsafe {
1120 buf.set_len(buf_read);
1121 }
e9174d1e
SL
1122
1123 if buf_read != buf.capacity() {
1124 buf.shrink_to_fit();
1125
1126 return Ok(PathBuf::from(OsString::from_vec(buf)));
1127 }
1128
1129 // Trigger the internal buffer resizing logic of `Vec` by requiring
1130 // more space than the current capacity. The length is guaranteed to be
1131 // the same as the capacity due to the if statement above.
1132 buf.reserve(1);
85aaf69f 1133 }
85aaf69f
SL
1134}
1135
fc512014
XL
1136pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
1137 let original = cstr(original)?;
1138 let link = cstr(link)?;
1139 cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) })?;
85aaf69f
SL
1140 Ok(())
1141}
1142
fc512014
XL
1143pub fn link(original: &Path, link: &Path) -> io::Result<()> {
1144 let original = cstr(original)?;
1145 let link = cstr(link)?;
29967ef6 1146 cfg_if::cfg_if! {
94222f64
XL
1147 if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf"))] {
1148 // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
136023e0
XL
1149 // it implementation-defined whether `link` follows symlinks, so rely on the
1150 // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
1151 // Android has `linkat` on newer versions, but we happen to know `link`
1152 // always has the correct behavior, so it's here as well.
fc512014 1153 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
136023e0
XL
1154 } else if #[cfg(target_os = "macos")] {
1155 // On MacOS, older versions (<=10.9) lack support for linkat while newer
1156 // versions have it. We want to use linkat if it is available, so we use weak!
1157 // to check. `linkat` is preferable to `link` ecause it gives us a flag to
1158 // specify how symlinks should be handled. We pass 0 as the flags argument,
1159 // meaning it shouldn't follow symlinks.
1160 weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
1161
1162 if let Some(f) = linkat.get() {
1163 cvt(unsafe { f(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
1164 } else {
1165 cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
1166 };
29967ef6 1167 } else {
136023e0
XL
1168 // Where we can, use `linkat` instead of `link`; see the comment above
1169 // this one for details on why.
fc512014 1170 cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
29967ef6
XL
1171 }
1172 }
85aaf69f
SL
1173 Ok(())
1174}
1175
1176pub fn stat(p: &Path) -> io::Result<FileAttr> {
54a0048b 1177 let p = cstr(p)?;
e74abb32
XL
1178
1179 cfg_has_statx! {
1180 if let Some(ret) = unsafe { try_statx(
1181 libc::AT_FDCWD,
1182 p.as_ptr(),
1183 libc::AT_STATX_SYNC_AS_STAT,
1184 libc::STATX_ALL,
1185 ) } {
1186 return ret;
1187 }
1188 }
1189
7453a54e 1190 let mut stat: stat64 = unsafe { mem::zeroed() };
dfeec247 1191 cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
e74abb32 1192 Ok(FileAttr::from_stat64(stat))
85aaf69f
SL
1193}
1194
1195pub fn lstat(p: &Path) -> io::Result<FileAttr> {
54a0048b 1196 let p = cstr(p)?;
e74abb32
XL
1197
1198 cfg_has_statx! {
1199 if let Some(ret) = unsafe { try_statx(
1200 libc::AT_FDCWD,
1201 p.as_ptr(),
1202 libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
1203 libc::STATX_ALL,
1204 ) } {
1205 return ret;
1206 }
1207 }
1208
7453a54e 1209 let mut stat: stat64 = unsafe { mem::zeroed() };
dfeec247 1210 cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
e74abb32 1211 Ok(FileAttr::from_stat64(stat))
85aaf69f
SL
1212}
1213
d9579d0f 1214pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
54a0048b 1215 let path = CString::new(p.as_os_str().as_bytes())?;
e9174d1e 1216 let buf;
d9579d0f 1217 unsafe {
92a42be0 1218 let r = libc::realpath(path.as_ptr(), ptr::null_mut());
d9579d0f 1219 if r.is_null() {
dfeec247 1220 return Err(io::Error::last_os_error());
d9579d0f 1221 }
e9174d1e
SL
1222 buf = CStr::from_ptr(r).to_bytes().to_vec();
1223 libc::free(r as *mut _);
d9579d0f 1224 }
d9579d0f
AL
1225 Ok(PathBuf::from(OsString::from_vec(buf)))
1226}
c1a9b12d 1227
48663c56
XL
1228fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1229 use crate::fs::File;
cdc7bbd5 1230 use crate::sys_common::fs::NOT_FILE_ERROR;
48663c56
XL
1231
1232 let reader = File::open(from)?;
1233 let metadata = reader.metadata()?;
1234 if !metadata.is_file() {
cdc7bbd5 1235 return Err(NOT_FILE_ERROR);
48663c56
XL
1236 }
1237 Ok((reader, metadata))
1238}
1239
94222f64
XL
1240#[cfg(target_os = "espidf")]
1241fn open_to_and_set_permissions(
1242 to: &Path,
1243 reader_metadata: crate::fs::Metadata,
1244) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1245 use crate::fs::OpenOptions;
1246 let writer = OpenOptions::new().open(to)?;
1247 let writer_metadata = writer.metadata()?;
1248 Ok((writer, writer_metadata))
1249}
1250
1251#[cfg(not(target_os = "espidf"))]
48663c56 1252fn open_to_and_set_permissions(
532ac7d7 1253 to: &Path,
48663c56
XL
1254 reader_metadata: crate::fs::Metadata,
1255) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
1256 use crate::fs::OpenOptions;
532ac7d7
XL
1257 use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
1258
48663c56 1259 let perm = reader_metadata.permissions();
532ac7d7
XL
1260 let writer = OpenOptions::new()
1261 // create the file with the correct mode right away
1262 .mode(perm.mode())
1263 .write(true)
1264 .create(true)
1265 .truncate(true)
1266 .open(to)?;
1267 let writer_metadata = writer.metadata()?;
1268 if writer_metadata.is_file() {
1269 // Set the correct file permissions, in case the file already existed.
1270 // Don't set the permissions on already existing non-files like
1271 // pipes/FIFOs or device nodes.
1272 writer.set_permissions(perm)?;
c1a9b12d 1273 }
48663c56 1274 Ok((writer, writer_metadata))
532ac7d7 1275}
c1a9b12d 1276
dfeec247
XL
1277#[cfg(not(any(
1278 target_os = "linux",
1279 target_os = "android",
1280 target_os = "macos",
1281 target_os = "ios"
1282)))]
532ac7d7 1283pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
48663c56
XL
1284 let (mut reader, reader_metadata) = open_from(from)?;
1285 let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
c1a9b12d 1286
532ac7d7 1287 io::copy(&mut reader, &mut writer)
c1a9b12d 1288}
94b46f34
XL
1289
1290#[cfg(any(target_os = "linux", target_os = "android"))]
1291pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
48663c56 1292 let (mut reader, reader_metadata) = open_from(from)?;
1b1a35ee 1293 let max_len = u64::MAX;
48663c56 1294 let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
94b46f34 1295
fc512014
XL
1296 use super::kernel_copy::{copy_regular_files, CopyResult};
1297
1298 match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
1299 CopyResult::Ended(bytes) => Ok(bytes),
1300 CopyResult::Error(e, _) => Err(e),
1301 CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) {
1302 Ok(bytes) => Ok(bytes + written),
1303 Err(e) => Err(e),
1304 },
94b46f34 1305 }
94b46f34 1306}
532ac7d7
XL
1307
1308#[cfg(any(target_os = "macos", target_os = "ios"))]
1309pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
48663c56
XL
1310 use crate::sync::atomic::{AtomicBool, Ordering};
1311
532ac7d7
XL
1312 const COPYFILE_ACL: u32 = 1 << 0;
1313 const COPYFILE_STAT: u32 = 1 << 1;
1314 const COPYFILE_XATTR: u32 = 1 << 2;
1315 const COPYFILE_DATA: u32 = 1 << 3;
1316
1317 const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL;
1318 const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR;
1319 const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA;
1320
1321 const COPYFILE_STATE_COPIED: u32 = 8;
1322
1323 #[allow(non_camel_case_types)]
1324 type copyfile_state_t = *mut libc::c_void;
1325 #[allow(non_camel_case_types)]
1326 type copyfile_flags_t = u32;
1327
1328 extern "C" {
1329 fn fcopyfile(
1330 from: libc::c_int,
1331 to: libc::c_int,
1332 state: copyfile_state_t,
1333 flags: copyfile_flags_t,
1334 ) -> libc::c_int;
1335 fn copyfile_state_alloc() -> copyfile_state_t;
1336 fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int;
1337 fn copyfile_state_get(
1338 state: copyfile_state_t,
1339 flag: u32,
1340 dst: *mut libc::c_void,
1341 ) -> libc::c_int;
1342 }
1343
1344 struct FreeOnDrop(copyfile_state_t);
1345 impl Drop for FreeOnDrop {
1346 fn drop(&mut self) {
1347 // The code below ensures that `FreeOnDrop` is never a null pointer
1348 unsafe {
1349 // `copyfile_state_free` returns -1 if the `to` or `from` files
60c5eb7d 1350 // cannot be closed. However, this is not considered this an
532ac7d7
XL
1351 // error.
1352 copyfile_state_free(self.0);
1353 }
1354 }
1355 }
1356
48663c56
XL
1357 // MacOS prior to 10.12 don't support `fclonefileat`
1358 // We store the availability in a global to avoid unnecessary syscalls
1359 static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true);
1360 syscall! {
1361 fn fclonefileat(
1362 srcfd: libc::c_int,
1363 dst_dirfd: libc::c_int,
136023e0 1364 dst: *const c_char,
48663c56
XL
1365 flags: libc::c_int
1366 ) -> libc::c_int
1367 }
1368
1369 let (reader, reader_metadata) = open_from(from)?;
1370
1371 // Opportunistically attempt to create a copy-on-write clone of `from`
1372 // using `fclonefileat`.
1373 if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
1374 let to = cstr(to)?;
dfeec247
XL
1375 let clonefile_result =
1376 cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) });
48663c56
XL
1377 match clonefile_result {
1378 Ok(_) => return Ok(reader_metadata.len()),
1379 Err(err) => match err.raw_os_error() {
1380 // `fclonefileat` will fail on non-APFS volumes, if the
1381 // destination already exists, or if the source and destination
1382 // are on different devices. In all these cases `fcopyfile`
1383 // should succeed.
1384 Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
1385 Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed),
1386 _ => return Err(err),
dfeec247 1387 },
48663c56
XL
1388 }
1389 }
1390
1391 // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
1392 let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?;
532ac7d7
XL
1393
1394 // We ensure that `FreeOnDrop` never contains a null pointer so it is
1395 // always safe to call `copyfile_state_free`
1396 let state = unsafe {
1397 let state = copyfile_state_alloc();
1398 if state.is_null() {
1399 return Err(crate::io::Error::last_os_error());
1400 }
1401 FreeOnDrop(state)
1402 };
1403
dfeec247 1404 let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA };
532ac7d7 1405
dfeec247 1406 cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
532ac7d7
XL
1407
1408 let mut bytes_copied: libc::off_t = 0;
1409 cvt(unsafe {
1410 copyfile_state_get(
1411 state.0,
1412 COPYFILE_STATE_COPIED,
1413 &mut bytes_copied as *mut libc::off_t as *mut libc::c_void,
1414 )
1415 })?;
1416 Ok(bytes_copied as u64)
1417}
cdc7bbd5 1418
17df50a5 1419#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
cdc7bbd5
XL
1420pub fn chroot(dir: &Path) -> io::Result<()> {
1421 let dir = cstr(dir)?;
1422 cvt(unsafe { libc::chroot(dir.as_ptr()) })?;
1423 Ok(())
1424}