]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/fs.rs
Rollup merge of #59600 - tobia:master, r=pnkfelix
[rust.git] / src / libstd / sys / unix / fs.rs
1 use crate::os::unix::prelude::*;
2
3 use crate::ffi::{CString, CStr, OsString, OsStr};
4 use crate::fmt;
5 use crate::io::{self, Error, ErrorKind, SeekFrom, IoSlice, IoSliceMut};
6 use crate::mem;
7 use crate::path::{Path, PathBuf};
8 use crate::ptr;
9 use crate::sync::Arc;
10 use crate::sys::fd::FileDesc;
11 use crate::sys::time::SystemTime;
12 use crate::sys::{cvt, cvt_r};
13 use crate::sys_common::{AsInner, FromInner};
14
15 use libc::{c_int, mode_t};
16
17 #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
18 use libc::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64};
19 #[cfg(any(target_os = "linux", target_os = "emscripten"))]
20 use libc::fstatat64;
21 #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
22 use libc::dirfd;
23 #[cfg(target_os = "android")]
24 use libc::{stat as stat64, fstat as fstat64, fstatat as fstatat64, lstat as lstat64, lseek64,
25            dirent as dirent64, open as open64};
26 #[cfg(not(any(target_os = "linux",
27               target_os = "emscripten",
28               target_os = "l4re",
29               target_os = "android")))]
30 use libc::{stat as stat64, fstat as fstat64, lstat as lstat64, off_t as off64_t,
31            ftruncate as ftruncate64, lseek as lseek64, dirent as dirent64, open as open64};
32 #[cfg(not(any(target_os = "linux",
33               target_os = "emscripten",
34               target_os = "solaris",
35               target_os = "l4re",
36               target_os = "fuchsia")))]
37 use libc::{readdir_r as readdir64_r};
38
39 pub use crate::sys_common::fs::remove_dir_all;
40
41 pub struct File(FileDesc);
42
43 #[derive(Clone)]
44 pub struct FileAttr {
45     stat: stat64,
46 }
47
48 // all DirEntry's will have a reference to this struct
49 struct InnerReadDir {
50     dirp: Dir,
51     root: PathBuf,
52 }
53
54 #[derive(Clone)]
55 pub struct ReadDir {
56     inner: Arc<InnerReadDir>,
57     end_of_stream: bool,
58 }
59
60 struct Dir(*mut libc::DIR);
61
62 unsafe impl Send for Dir {}
63 unsafe impl Sync for Dir {}
64
65 pub struct DirEntry {
66     entry: dirent64,
67     dir: ReadDir,
68     // We need to store an owned copy of the entry name
69     // on Solaris and Fuchsia because a) it uses a zero-length
70     // array to store the name, b) its lifetime between readdir
71     // calls is not guaranteed.
72     #[cfg(any(target_os = "solaris", target_os = "fuchsia"))]
73     name: Box<[u8]>
74 }
75
76 #[derive(Clone, Debug)]
77 pub struct OpenOptions {
78     // generic
79     read: bool,
80     write: bool,
81     append: bool,
82     truncate: bool,
83     create: bool,
84     create_new: bool,
85     // system-specific
86     custom_flags: i32,
87     mode: mode_t,
88 }
89
90 #[derive(Clone, PartialEq, Eq, Debug)]
91 pub struct FilePermissions { mode: mode_t }
92
93 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
94 pub struct FileType { mode: mode_t }
95
96 #[derive(Debug)]
97 pub struct DirBuilder { mode: mode_t }
98
99 impl FileAttr {
100     pub fn size(&self) -> u64 { self.stat.st_size as u64 }
101     pub fn perm(&self) -> FilePermissions {
102         FilePermissions { mode: (self.stat.st_mode as mode_t) }
103     }
104
105     pub fn file_type(&self) -> FileType {
106         FileType { mode: self.stat.st_mode as mode_t }
107     }
108 }
109
110 #[cfg(target_os = "netbsd")]
111 impl FileAttr {
112     pub fn modified(&self) -> io::Result<SystemTime> {
113         Ok(SystemTime::from(libc::timespec {
114             tv_sec: self.stat.st_mtime as libc::time_t,
115             tv_nsec: self.stat.st_mtimensec as libc::c_long,
116         }))
117     }
118
119     pub fn accessed(&self) -> io::Result<SystemTime> {
120         Ok(SystemTime::from(libc::timespec {
121             tv_sec: self.stat.st_atime as libc::time_t,
122             tv_nsec: self.stat.st_atimensec as libc::c_long,
123         }))
124     }
125
126     pub fn created(&self) -> io::Result<SystemTime> {
127         Ok(SystemTime::from(libc::timespec {
128             tv_sec: self.stat.st_birthtime as libc::time_t,
129             tv_nsec: self.stat.st_birthtimensec as libc::c_long,
130         }))
131     }
132 }
133
134 #[cfg(not(target_os = "netbsd"))]
135 impl FileAttr {
136     pub fn modified(&self) -> io::Result<SystemTime> {
137         Ok(SystemTime::from(libc::timespec {
138             tv_sec: self.stat.st_mtime as libc::time_t,
139             tv_nsec: self.stat.st_mtime_nsec as _,
140         }))
141     }
142
143     pub fn accessed(&self) -> io::Result<SystemTime> {
144         Ok(SystemTime::from(libc::timespec {
145             tv_sec: self.stat.st_atime as libc::time_t,
146             tv_nsec: self.stat.st_atime_nsec as _,
147         }))
148     }
149
150     #[cfg(any(target_os = "freebsd",
151               target_os = "openbsd",
152               target_os = "macos",
153               target_os = "ios"))]
154     pub fn created(&self) -> io::Result<SystemTime> {
155         Ok(SystemTime::from(libc::timespec {
156             tv_sec: self.stat.st_birthtime as libc::time_t,
157             tv_nsec: self.stat.st_birthtime_nsec as libc::c_long,
158         }))
159     }
160
161     #[cfg(not(any(target_os = "freebsd",
162                   target_os = "openbsd",
163                   target_os = "macos",
164                   target_os = "ios")))]
165     pub fn created(&self) -> io::Result<SystemTime> {
166         Err(io::Error::new(io::ErrorKind::Other,
167                            "creation time is not available on this platform \
168                             currently"))
169     }
170 }
171
172 impl AsInner<stat64> for FileAttr {
173     fn as_inner(&self) -> &stat64 { &self.stat }
174 }
175
176 impl FilePermissions {
177     pub fn readonly(&self) -> bool {
178         // check if any class (owner, group, others) has write permission
179         self.mode & 0o222 == 0
180     }
181
182     pub fn set_readonly(&mut self, readonly: bool) {
183         if readonly {
184             // remove write permission for all classes; equivalent to `chmod a-w <file>`
185             self.mode &= !0o222;
186         } else {
187             // add write permission for all classes; equivalent to `chmod a+w <file>`
188             self.mode |= 0o222;
189         }
190     }
191     pub fn mode(&self) -> u32 { self.mode as u32 }
192 }
193
194 impl FileType {
195     pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) }
196     pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) }
197     pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) }
198
199     pub fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode }
200 }
201
202 impl FromInner<u32> for FilePermissions {
203     fn from_inner(mode: u32) -> FilePermissions {
204         FilePermissions { mode: mode as mode_t }
205     }
206 }
207
208 impl fmt::Debug for ReadDir {
209     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210         // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
211         // Thus the result will be e g 'ReadDir("/home")'
212         fmt::Debug::fmt(&*self.inner.root, f)
213     }
214 }
215
216 impl Iterator for ReadDir {
217     type Item = io::Result<DirEntry>;
218
219     #[cfg(any(target_os = "solaris", target_os = "fuchsia"))]
220     fn next(&mut self) -> Option<io::Result<DirEntry>> {
221         use crate::slice;
222
223         unsafe {
224             loop {
225                 // Although readdir_r(3) would be a correct function to use here because
226                 // of the thread safety, on Illumos and Fuchsia the readdir(3C) function
227                 // is safe to use in threaded applications and it is generally preferred
228                 // over the readdir_r(3C) function.
229                 super::os::set_errno(0);
230                 let entry_ptr = libc::readdir(self.inner.dirp.0);
231                 if entry_ptr.is_null() {
232                     // NULL can mean either the end is reached or an error occurred.
233                     // So we had to clear errno beforehand to check for an error now.
234                     return match super::os::errno() {
235                         0 => None,
236                         e => Some(Err(Error::from_raw_os_error(e))),
237                     }
238                 }
239
240                 let name = (*entry_ptr).d_name.as_ptr();
241                 let namelen = libc::strlen(name) as usize;
242
243                 let ret = DirEntry {
244                     entry: *entry_ptr,
245                     name: slice::from_raw_parts(name as *const u8,
246                                                 namelen as usize).to_owned().into_boxed_slice(),
247                     dir: self.clone()
248                 };
249                 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
250                     return Some(Ok(ret))
251                 }
252             }
253         }
254     }
255
256     #[cfg(not(any(target_os = "solaris", target_os = "fuchsia")))]
257     fn next(&mut self) -> Option<io::Result<DirEntry>> {
258         if self.end_of_stream {
259             return None;
260         }
261
262         unsafe {
263             let mut ret = DirEntry {
264                 entry: mem::zeroed(),
265                 dir: self.clone(),
266             };
267             let mut entry_ptr = ptr::null_mut();
268             loop {
269                 if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
270                     if entry_ptr.is_null() {
271                         // We encountered an error (which will be returned in this iteration), but
272                         // we also reached the end of the directory stream. The `end_of_stream`
273                         // flag is enabled to make sure that we return `None` in the next iteration
274                         // (instead of looping forever)
275                         self.end_of_stream = true;
276                     }
277                     return Some(Err(Error::last_os_error()))
278                 }
279                 if entry_ptr.is_null() {
280                     return None
281                 }
282                 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
283                     return Some(Ok(ret))
284                 }
285             }
286         }
287     }
288 }
289
290 impl Drop for Dir {
291     fn drop(&mut self) {
292         let r = unsafe { libc::closedir(self.0) };
293         debug_assert_eq!(r, 0);
294     }
295 }
296
297 impl DirEntry {
298     pub fn path(&self) -> PathBuf {
299         self.dir.inner.root.join(OsStr::from_bytes(self.name_bytes()))
300     }
301
302     pub fn file_name(&self) -> OsString {
303         OsStr::from_bytes(self.name_bytes()).to_os_string()
304     }
305
306     #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
307     pub fn metadata(&self) -> io::Result<FileAttr> {
308         let fd = cvt(unsafe {dirfd(self.dir.inner.dirp.0)})?;
309         let mut stat: stat64 = unsafe { mem::zeroed() };
310         cvt(unsafe {
311             fstatat64(fd, self.entry.d_name.as_ptr(), &mut stat, libc::AT_SYMLINK_NOFOLLOW)
312         })?;
313         Ok(FileAttr { stat })
314     }
315
316     #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
317     pub fn metadata(&self) -> io::Result<FileAttr> {
318         lstat(&self.path())
319     }
320
321     #[cfg(any(target_os = "solaris", target_os = "haiku", target_os = "hermit"))]
322     pub fn file_type(&self) -> io::Result<FileType> {
323         lstat(&self.path()).map(|m| m.file_type())
324     }
325
326     #[cfg(not(any(target_os = "solaris", target_os = "haiku", target_os = "hermit")))]
327     pub fn file_type(&self) -> io::Result<FileType> {
328         match self.entry.d_type {
329             libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
330             libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
331             libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
332             libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
333             libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
334             libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
335             libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
336             _ => lstat(&self.path()).map(|m| m.file_type()),
337         }
338     }
339
340     #[cfg(any(target_os = "macos",
341               target_os = "ios",
342               target_os = "linux",
343               target_os = "emscripten",
344               target_os = "android",
345               target_os = "solaris",
346               target_os = "haiku",
347               target_os = "l4re",
348               target_os = "fuchsia",
349               target_os = "hermit"))]
350     pub fn ino(&self) -> u64 {
351         self.entry.d_ino as u64
352     }
353
354     #[cfg(any(target_os = "freebsd",
355               target_os = "openbsd",
356               target_os = "netbsd",
357               target_os = "dragonfly"))]
358     pub fn ino(&self) -> u64 {
359         self.entry.d_fileno as u64
360     }
361
362     #[cfg(any(target_os = "macos",
363               target_os = "ios",
364               target_os = "netbsd",
365               target_os = "openbsd",
366               target_os = "freebsd",
367               target_os = "dragonfly"))]
368     fn name_bytes(&self) -> &[u8] {
369         use crate::slice;
370         unsafe {
371             slice::from_raw_parts(self.entry.d_name.as_ptr() as *const u8,
372                                   self.entry.d_namlen as usize)
373         }
374     }
375     #[cfg(any(target_os = "android",
376               target_os = "linux",
377               target_os = "emscripten",
378               target_os = "l4re",
379               target_os = "haiku",
380               target_os = "hermit"))]
381     fn name_bytes(&self) -> &[u8] {
382         unsafe {
383             CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes()
384         }
385     }
386     #[cfg(any(target_os = "solaris",
387               target_os = "fuchsia"))]
388     fn name_bytes(&self) -> &[u8] {
389         &*self.name
390     }
391 }
392
393 impl OpenOptions {
394     pub fn new() -> OpenOptions {
395         OpenOptions {
396             // generic
397             read: false,
398             write: false,
399             append: false,
400             truncate: false,
401             create: false,
402             create_new: false,
403             // system-specific
404             custom_flags: 0,
405             mode: 0o666,
406         }
407     }
408
409     pub fn read(&mut self, read: bool) { self.read = read; }
410     pub fn write(&mut self, write: bool) { self.write = write; }
411     pub fn append(&mut self, append: bool) { self.append = append; }
412     pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; }
413     pub fn create(&mut self, create: bool) { self.create = create; }
414     pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; }
415
416     pub fn custom_flags(&mut self, flags: i32) { self.custom_flags = flags; }
417     pub fn mode(&mut self, mode: u32) { self.mode = mode as mode_t; }
418
419     fn get_access_mode(&self) -> io::Result<c_int> {
420         match (self.read, self.write, self.append) {
421             (true,  false, false) => Ok(libc::O_RDONLY),
422             (false, true,  false) => Ok(libc::O_WRONLY),
423             (true,  true,  false) => Ok(libc::O_RDWR),
424             (false, _,     true)  => Ok(libc::O_WRONLY | libc::O_APPEND),
425             (true,  _,     true)  => Ok(libc::O_RDWR | libc::O_APPEND),
426             (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
427         }
428     }
429
430     fn get_creation_mode(&self) -> io::Result<c_int> {
431         match (self.write, self.append) {
432             (true, false) => {}
433             (false, false) =>
434                 if self.truncate || self.create || self.create_new {
435                     return Err(Error::from_raw_os_error(libc::EINVAL));
436                 },
437             (_, true) =>
438                 if self.truncate && !self.create_new {
439                     return Err(Error::from_raw_os_error(libc::EINVAL));
440                 },
441         }
442
443         Ok(match (self.create, self.truncate, self.create_new) {
444                 (false, false, false) => 0,
445                 (true,  false, false) => libc::O_CREAT,
446                 (false, true,  false) => libc::O_TRUNC,
447                 (true,  true,  false) => libc::O_CREAT | libc::O_TRUNC,
448                 (_,      _,    true)  => libc::O_CREAT | libc::O_EXCL,
449            })
450     }
451 }
452
453 impl File {
454     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
455         let path = cstr(path)?;
456         File::open_c(&path, opts)
457     }
458
459     pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
460         let flags = libc::O_CLOEXEC |
461                     opts.get_access_mode()? |
462                     opts.get_creation_mode()? |
463                     (opts.custom_flags as c_int & !libc::O_ACCMODE);
464         let fd = cvt_r(|| unsafe {
465             open64(path.as_ptr(), flags, opts.mode as c_int)
466         })?;
467         let fd = FileDesc::new(fd);
468
469         // Currently the standard library supports Linux 2.6.18 which did not
470         // have the O_CLOEXEC flag (passed above). If we're running on an older
471         // Linux kernel then the flag is just ignored by the OS. After we open
472         // the first file, we check whether it has CLOEXEC set. If it doesn't,
473         // we will explicitly ask for a CLOEXEC fd for every further file we
474         // open, if it does, we will skip that step.
475         //
476         // The CLOEXEC flag, however, is supported on versions of macOS/BSD/etc
477         // that we support, so we only do this on Linux currently.
478         #[cfg(target_os = "linux")]
479         fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> {
480             use crate::sync::atomic::{AtomicUsize, Ordering};
481
482             const OPEN_CLOEXEC_UNKNOWN: usize = 0;
483             const OPEN_CLOEXEC_SUPPORTED: usize = 1;
484             const OPEN_CLOEXEC_NOTSUPPORTED: usize = 2;
485             static OPEN_CLOEXEC: AtomicUsize = AtomicUsize::new(OPEN_CLOEXEC_UNKNOWN);
486
487             let need_to_set;
488             match OPEN_CLOEXEC.load(Ordering::Relaxed) {
489                 OPEN_CLOEXEC_UNKNOWN => {
490                     need_to_set = !fd.get_cloexec()?;
491                     OPEN_CLOEXEC.store(if need_to_set {
492                         OPEN_CLOEXEC_NOTSUPPORTED
493                     } else {
494                         OPEN_CLOEXEC_SUPPORTED
495                     }, Ordering::Relaxed);
496                 },
497                 OPEN_CLOEXEC_SUPPORTED => need_to_set = false,
498                 OPEN_CLOEXEC_NOTSUPPORTED => need_to_set = true,
499                 _ => unreachable!(),
500             }
501             if need_to_set {
502                 fd.set_cloexec()?;
503             }
504             Ok(())
505         }
506
507         #[cfg(not(target_os = "linux"))]
508         fn ensure_cloexec(_: &FileDesc) -> io::Result<()> {
509             Ok(())
510         }
511
512         ensure_cloexec(&fd)?;
513         Ok(File(fd))
514     }
515
516     pub fn file_attr(&self) -> io::Result<FileAttr> {
517         let mut stat: stat64 = unsafe { mem::zeroed() };
518         cvt(unsafe {
519             fstat64(self.0.raw(), &mut stat)
520         })?;
521         Ok(FileAttr { stat })
522     }
523
524     pub fn fsync(&self) -> io::Result<()> {
525         cvt_r(|| unsafe { os_fsync(self.0.raw()) })?;
526         return Ok(());
527
528         #[cfg(any(target_os = "macos", target_os = "ios"))]
529         unsafe fn os_fsync(fd: c_int) -> c_int {
530             libc::fcntl(fd, libc::F_FULLFSYNC)
531         }
532         #[cfg(not(any(target_os = "macos", target_os = "ios")))]
533         unsafe fn os_fsync(fd: c_int) -> c_int { libc::fsync(fd) }
534     }
535
536     pub fn datasync(&self) -> io::Result<()> {
537         cvt_r(|| unsafe { os_datasync(self.0.raw()) })?;
538         return Ok(());
539
540         #[cfg(any(target_os = "macos", target_os = "ios"))]
541         unsafe fn os_datasync(fd: c_int) -> c_int {
542             libc::fcntl(fd, libc::F_FULLFSYNC)
543         }
544         #[cfg(target_os = "linux")]
545         unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) }
546         #[cfg(not(any(target_os = "macos",
547                       target_os = "ios",
548                       target_os = "linux")))]
549         unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) }
550     }
551
552     pub fn truncate(&self, size: u64) -> io::Result<()> {
553         #[cfg(target_os = "android")]
554         return crate::sys::android::ftruncate64(self.0.raw(), size);
555
556         #[cfg(not(target_os = "android"))]
557         return cvt_r(|| unsafe {
558             ftruncate64(self.0.raw(), size as off64_t)
559         }).map(|_| ());
560     }
561
562     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
563         self.0.read(buf)
564     }
565
566     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
567         self.0.read_vectored(bufs)
568     }
569
570     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
571         self.0.read_at(buf, offset)
572     }
573
574     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
575         self.0.write(buf)
576     }
577
578     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
579         self.0.write_vectored(bufs)
580     }
581
582     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
583         self.0.write_at(buf, offset)
584     }
585
586     pub fn flush(&self) -> io::Result<()> { Ok(()) }
587
588     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
589         let (whence, pos) = match pos {
590             // Casting to `i64` is fine, too large values will end up as
591             // negative which will cause an error in `lseek64`.
592             SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
593             SeekFrom::End(off) => (libc::SEEK_END, off),
594             SeekFrom::Current(off) => (libc::SEEK_CUR, off),
595         };
596         #[cfg(target_os = "emscripten")]
597         let pos = pos as i32;
598         let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?;
599         Ok(n as u64)
600     }
601
602     pub fn duplicate(&self) -> io::Result<File> {
603         self.0.duplicate().map(File)
604     }
605
606     pub fn fd(&self) -> &FileDesc { &self.0 }
607
608     pub fn into_fd(self) -> FileDesc { self.0 }
609
610     pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
611         cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?;
612         Ok(())
613     }
614 }
615
616 impl DirBuilder {
617     pub fn new() -> DirBuilder {
618         DirBuilder { mode: 0o777 }
619     }
620
621     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
622         let p = cstr(p)?;
623         cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
624         Ok(())
625     }
626
627     pub fn set_mode(&mut self, mode: u32) {
628         self.mode = mode as mode_t;
629     }
630 }
631
632 fn cstr(path: &Path) -> io::Result<CString> {
633     Ok(CString::new(path.as_os_str().as_bytes())?)
634 }
635
636 impl FromInner<c_int> for File {
637     fn from_inner(fd: c_int) -> File {
638         File(FileDesc::new(fd))
639     }
640 }
641
642 impl fmt::Debug for File {
643     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644         #[cfg(target_os = "linux")]
645         fn get_path(fd: c_int) -> Option<PathBuf> {
646             let mut p = PathBuf::from("/proc/self/fd");
647             p.push(&fd.to_string());
648             readlink(&p).ok()
649         }
650
651         #[cfg(target_os = "macos")]
652         fn get_path(fd: c_int) -> Option<PathBuf> {
653             // FIXME: The use of PATH_MAX is generally not encouraged, but it
654             // is inevitable in this case because macOS defines `fcntl` with
655             // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
656             // alternatives. If a better method is invented, it should be used
657             // instead.
658             let mut buf = vec![0;libc::PATH_MAX as usize];
659             let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
660             if n == -1 {
661                 return None;
662             }
663             let l = buf.iter().position(|&c| c == 0).unwrap();
664             buf.truncate(l as usize);
665             buf.shrink_to_fit();
666             Some(PathBuf::from(OsString::from_vec(buf)))
667         }
668
669         #[cfg(not(any(target_os = "linux", target_os = "macos")))]
670         fn get_path(_fd: c_int) -> Option<PathBuf> {
671             // FIXME(#24570): implement this for other Unix platforms
672             None
673         }
674
675         #[cfg(any(target_os = "linux", target_os = "macos"))]
676         fn get_mode(fd: c_int) -> Option<(bool, bool)> {
677             let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
678             if mode == -1 {
679                 return None;
680             }
681             match mode & libc::O_ACCMODE {
682                 libc::O_RDONLY => Some((true, false)),
683                 libc::O_RDWR => Some((true, true)),
684                 libc::O_WRONLY => Some((false, true)),
685                 _ => None
686             }
687         }
688
689         #[cfg(not(any(target_os = "linux", target_os = "macos")))]
690         fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
691             // FIXME(#24570): implement this for other Unix platforms
692             None
693         }
694
695         let fd = self.0.raw();
696         let mut b = f.debug_struct("File");
697         b.field("fd", &fd);
698         if let Some(path) = get_path(fd) {
699             b.field("path", &path);
700         }
701         if let Some((read, write)) = get_mode(fd) {
702             b.field("read", &read).field("write", &write);
703         }
704         b.finish()
705     }
706 }
707
708 pub fn readdir(p: &Path) -> io::Result<ReadDir> {
709     let root = p.to_path_buf();
710     let p = cstr(p)?;
711     unsafe {
712         let ptr = libc::opendir(p.as_ptr());
713         if ptr.is_null() {
714             Err(Error::last_os_error())
715         } else {
716             let inner = InnerReadDir { dirp: Dir(ptr), root };
717             Ok(ReadDir{
718                 inner: Arc::new(inner),
719                 end_of_stream: false,
720             })
721         }
722     }
723 }
724
725 pub fn unlink(p: &Path) -> io::Result<()> {
726     let p = cstr(p)?;
727     cvt(unsafe { libc::unlink(p.as_ptr()) })?;
728     Ok(())
729 }
730
731 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
732     let old = cstr(old)?;
733     let new = cstr(new)?;
734     cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
735     Ok(())
736 }
737
738 pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
739     let p = cstr(p)?;
740     cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
741     Ok(())
742 }
743
744 pub fn rmdir(p: &Path) -> io::Result<()> {
745     let p = cstr(p)?;
746     cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
747     Ok(())
748 }
749
750 pub fn readlink(p: &Path) -> io::Result<PathBuf> {
751     let c_path = cstr(p)?;
752     let p = c_path.as_ptr();
753
754     let mut buf = Vec::with_capacity(256);
755
756     loop {
757         let buf_read = cvt(unsafe {
758             libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity())
759         })? as usize;
760
761         unsafe { buf.set_len(buf_read); }
762
763         if buf_read != buf.capacity() {
764             buf.shrink_to_fit();
765
766             return Ok(PathBuf::from(OsString::from_vec(buf)));
767         }
768
769         // Trigger the internal buffer resizing logic of `Vec` by requiring
770         // more space than the current capacity. The length is guaranteed to be
771         // the same as the capacity due to the if statement above.
772         buf.reserve(1);
773     }
774 }
775
776 pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
777     let src = cstr(src)?;
778     let dst = cstr(dst)?;
779     cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?;
780     Ok(())
781 }
782
783 pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
784     let src = cstr(src)?;
785     let dst = cstr(dst)?;
786     cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
787     Ok(())
788 }
789
790 pub fn stat(p: &Path) -> io::Result<FileAttr> {
791     let p = cstr(p)?;
792     let mut stat: stat64 = unsafe { mem::zeroed() };
793     cvt(unsafe {
794         stat64(p.as_ptr(), &mut stat)
795     })?;
796     Ok(FileAttr { stat })
797 }
798
799 pub fn lstat(p: &Path) -> io::Result<FileAttr> {
800     let p = cstr(p)?;
801     let mut stat: stat64 = unsafe { mem::zeroed() };
802     cvt(unsafe {
803         lstat64(p.as_ptr(), &mut stat)
804     })?;
805     Ok(FileAttr { stat })
806 }
807
808 pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
809     let path = CString::new(p.as_os_str().as_bytes())?;
810     let buf;
811     unsafe {
812         let r = libc::realpath(path.as_ptr(), ptr::null_mut());
813         if r.is_null() {
814             return Err(io::Error::last_os_error())
815         }
816         buf = CStr::from_ptr(r).to_bytes().to_vec();
817         libc::free(r as *mut _);
818     }
819     Ok(PathBuf::from(OsString::from_vec(buf)))
820 }
821
822 fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
823     use crate::fs::File;
824
825     let reader = File::open(from)?;
826     let metadata = reader.metadata()?;
827     if !metadata.is_file() {
828         return Err(Error::new(
829             ErrorKind::InvalidInput,
830             "the source path is not an existing regular file",
831         ));
832     }
833     Ok((reader, metadata))
834 }
835
836 fn open_to_and_set_permissions(
837     to: &Path,
838     reader_metadata: crate::fs::Metadata,
839 ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
840     use crate::fs::OpenOptions;
841     use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
842
843     let perm = reader_metadata.permissions();
844     let writer = OpenOptions::new()
845         // create the file with the correct mode right away
846         .mode(perm.mode())
847         .write(true)
848         .create(true)
849         .truncate(true)
850         .open(to)?;
851     let writer_metadata = writer.metadata()?;
852     if writer_metadata.is_file() {
853         // Set the correct file permissions, in case the file already existed.
854         // Don't set the permissions on already existing non-files like
855         // pipes/FIFOs or device nodes.
856         writer.set_permissions(perm)?;
857     }
858     Ok((writer, writer_metadata))
859 }
860
861 #[cfg(not(any(target_os = "linux",
862               target_os = "android",
863               target_os = "macos",
864               target_os = "ios")))]
865 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
866     let (mut reader, reader_metadata) = open_from(from)?;
867     let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
868
869     io::copy(&mut reader, &mut writer)
870 }
871
872 #[cfg(any(target_os = "linux", target_os = "android"))]
873 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
874     use crate::cmp;
875     use crate::sync::atomic::{AtomicBool, Ordering};
876
877     // Kernel prior to 4.5 don't have copy_file_range
878     // We store the availability in a global to avoid unnecessary syscalls
879     static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true);
880
881     unsafe fn copy_file_range(
882         fd_in: libc::c_int,
883         off_in: *mut libc::loff_t,
884         fd_out: libc::c_int,
885         off_out: *mut libc::loff_t,
886         len: libc::size_t,
887         flags: libc::c_uint,
888     ) -> libc::c_long {
889         libc::syscall(
890             libc::SYS_copy_file_range,
891             fd_in,
892             off_in,
893             fd_out,
894             off_out,
895             len,
896             flags,
897         )
898     }
899
900     let (mut reader, reader_metadata) = open_from(from)?;
901     let len = reader_metadata.len();
902     let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
903
904     let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
905     let mut written = 0u64;
906     while written < len {
907         let copy_result = if has_copy_file_range {
908             let bytes_to_copy = cmp::min(len - written, usize::max_value() as u64) as usize;
909             let copy_result = unsafe {
910                 // We actually don't have to adjust the offsets,
911                 // because copy_file_range adjusts the file offset automatically
912                 cvt(copy_file_range(
913                     reader.as_raw_fd(),
914                     ptr::null_mut(),
915                     writer.as_raw_fd(),
916                     ptr::null_mut(),
917                     bytes_to_copy,
918                     0,
919                 ))
920             };
921             if let Err(ref copy_err) = copy_result {
922                 match copy_err.raw_os_error() {
923                     Some(libc::ENOSYS) | Some(libc::EPERM) => {
924                         HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
925                     }
926                     _ => {}
927                 }
928             }
929             copy_result
930         } else {
931             Err(io::Error::from_raw_os_error(libc::ENOSYS))
932         };
933         match copy_result {
934             Ok(ret) => written += ret as u64,
935             Err(err) => {
936                 match err.raw_os_error() {
937                     Some(os_err)
938                     if os_err == libc::ENOSYS
939                         || os_err == libc::EXDEV
940                         || os_err == libc::EINVAL
941                         || os_err == libc::EPERM =>
942                         {
943                             // Try fallback io::copy if either:
944                             // - Kernel version is < 4.5 (ENOSYS)
945                             // - Files are mounted on different fs (EXDEV)
946                             // - copy_file_range is disallowed, for example by seccomp (EPERM)
947                             // - copy_file_range cannot be used with pipes or device nodes (EINVAL)
948                             assert_eq!(written, 0);
949                             return io::copy(&mut reader, &mut writer);
950                         }
951                     _ => return Err(err),
952                 }
953             }
954         }
955     }
956     Ok(written)
957 }
958
959 #[cfg(any(target_os = "macos", target_os = "ios"))]
960 pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
961     use crate::sync::atomic::{AtomicBool, Ordering};
962
963     const COPYFILE_ACL: u32 = 1 << 0;
964     const COPYFILE_STAT: u32 = 1 << 1;
965     const COPYFILE_XATTR: u32 = 1 << 2;
966     const COPYFILE_DATA: u32 = 1 << 3;
967
968     const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL;
969     const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR;
970     const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA;
971
972     const COPYFILE_STATE_COPIED: u32 = 8;
973
974     #[allow(non_camel_case_types)]
975     type copyfile_state_t = *mut libc::c_void;
976     #[allow(non_camel_case_types)]
977     type copyfile_flags_t = u32;
978
979     extern "C" {
980         fn fcopyfile(
981             from: libc::c_int,
982             to: libc::c_int,
983             state: copyfile_state_t,
984             flags: copyfile_flags_t,
985         ) -> libc::c_int;
986         fn copyfile_state_alloc() -> copyfile_state_t;
987         fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int;
988         fn copyfile_state_get(
989             state: copyfile_state_t,
990             flag: u32,
991             dst: *mut libc::c_void,
992         ) -> libc::c_int;
993     }
994
995     struct FreeOnDrop(copyfile_state_t);
996     impl Drop for FreeOnDrop {
997         fn drop(&mut self) {
998             // The code below ensures that `FreeOnDrop` is never a null pointer
999             unsafe {
1000                 // `copyfile_state_free` returns -1 if the `to` or `from` files
1001                 // cannot be closed. However, this is not considerd this an
1002                 // error.
1003                 copyfile_state_free(self.0);
1004             }
1005         }
1006     }
1007
1008     // MacOS prior to 10.12 don't support `fclonefileat`
1009     // We store the availability in a global to avoid unnecessary syscalls
1010     static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true);
1011     syscall! {
1012         fn fclonefileat(
1013             srcfd: libc::c_int,
1014             dst_dirfd: libc::c_int,
1015             dst: *const libc::c_char,
1016             flags: libc::c_int
1017         ) -> libc::c_int
1018     }
1019
1020     let (reader, reader_metadata) = open_from(from)?;
1021
1022     // Opportunistically attempt to create a copy-on-write clone of `from`
1023     // using `fclonefileat`.
1024     if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
1025         let to = cstr(to)?;
1026         let clonefile_result = cvt(unsafe {
1027             fclonefileat(
1028                 reader.as_raw_fd(),
1029                 libc::AT_FDCWD,
1030                 to.as_ptr(),
1031                 0,
1032             )
1033         });
1034         match clonefile_result {
1035             Ok(_) => return Ok(reader_metadata.len()),
1036             Err(err) => match err.raw_os_error() {
1037                 // `fclonefileat` will fail on non-APFS volumes, if the
1038                 // destination already exists, or if the source and destination
1039                 // are on different devices. In all these cases `fcopyfile`
1040                 // should succeed.
1041                 Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
1042                 Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed),
1043                 _ => return Err(err),
1044             }
1045         }
1046     }
1047
1048     // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
1049     let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?;
1050
1051     // We ensure that `FreeOnDrop` never contains a null pointer so it is
1052     // always safe to call `copyfile_state_free`
1053     let state = unsafe {
1054         let state = copyfile_state_alloc();
1055         if state.is_null() {
1056             return Err(crate::io::Error::last_os_error());
1057         }
1058         FreeOnDrop(state)
1059     };
1060
1061     let flags = if writer_metadata.is_file() {
1062         COPYFILE_ALL
1063     } else {
1064         COPYFILE_DATA
1065     };
1066
1067     cvt(unsafe {
1068         fcopyfile(
1069             reader.as_raw_fd(),
1070             writer.as_raw_fd(),
1071             state.0,
1072             flags,
1073         )
1074     })?;
1075
1076     let mut bytes_copied: libc::off_t = 0;
1077     cvt(unsafe {
1078         copyfile_state_get(
1079             state.0,
1080             COPYFILE_STATE_COPIED,
1081             &mut bytes_copied as *mut libc::off_t as *mut libc::c_void,
1082         )
1083     })?;
1084     Ok(bytes_copied as u64)
1085 }