1 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use os
::unix
::prelude
::*;
13 use ffi
::{CString, CStr, OsString, OsStr}
;
15 use io
::{self, Error, ErrorKind, SeekFrom}
;
16 use libc
::{self, c_int, mode_t}
;
18 use path
::{Path, PathBuf}
;
21 use sys
::fd
::FileDesc
;
22 use sys
::time
::SystemTime
;
23 use sys
::{cvt, cvt_r}
;
24 use sys_common
::{AsInner, FromInner}
;
26 #[cfg(any(target_os = "linux", target_os = "emscripten"))]
27 use libc
::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64}
;
28 #[cfg(target_os = "android")]
29 use libc
::{stat
as stat64
, fstat
as fstat64
, lstat
as lstat64
, lseek64
,
30 dirent
as dirent64
, open
as open64
};
31 #[cfg(not(any(target_os = "linux",
32 target_os
= "emscripten",
33 target_os
= "android")))]
34 use libc
::{stat
as stat64
, fstat
as fstat64
, lstat
as lstat64
, off_t
as off64_t
,
35 ftruncate
as ftruncate64
, lseek
as lseek64
, dirent
as dirent64
, open
as open64
};
36 #[cfg(not(any(target_os = "linux",
37 target_os
= "emscripten",
38 target_os
= "solaris")))]
39 use libc
::{readdir_r as readdir64_r}
;
41 pub struct File(FileDesc
);
53 struct Dir(*mut libc
::DIR
);
55 unsafe impl Send
for Dir {}
56 unsafe impl Sync
for Dir {}
61 // We need to store an owned copy of the directory name
62 // on Solaris because a) it uses a zero-length array to
63 // store the name, b) its lifetime between readdir calls
65 #[cfg(target_os = "solaris")]
70 pub struct OpenOptions
{
83 #[derive(Clone, PartialEq, Eq, Debug)]
84 pub struct FilePermissions { mode: mode_t }
86 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
87 pub struct FileType { mode: mode_t }
89 pub struct DirBuilder { mode: mode_t }
92 pub fn size(&self) -> u64 { self.stat.st_size as u64 }
93 pub fn perm(&self) -> FilePermissions
{
94 FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 }
97 pub fn file_type(&self) -> FileType
{
98 FileType { mode: self.stat.st_mode as mode_t }
102 #[cfg(target_os = "netbsd")]
104 pub fn modified(&self) -> io
::Result
<SystemTime
> {
105 Ok(SystemTime
::from(libc
::timespec
{
106 tv_sec
: self.stat
.st_mtime
as libc
::time_t
,
107 tv_nsec
: self.stat
.st_mtimensec
as libc
::c_long
,
111 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
112 Ok(SystemTime
::from(libc
::timespec
{
113 tv_sec
: self.stat
.st_atime
as libc
::time_t
,
114 tv_nsec
: self.stat
.st_atimensec
as libc
::c_long
,
118 pub fn created(&self) -> io
::Result
<SystemTime
> {
119 Ok(SystemTime
::from(libc
::timespec
{
120 tv_sec
: self.stat
.st_birthtime
as libc
::time_t
,
121 tv_nsec
: self.stat
.st_birthtimensec
as libc
::c_long
,
126 #[cfg(not(target_os = "netbsd"))]
128 pub fn modified(&self) -> io
::Result
<SystemTime
> {
129 Ok(SystemTime
::from(libc
::timespec
{
130 tv_sec
: self.stat
.st_mtime
as libc
::time_t
,
131 tv_nsec
: self.stat
.st_mtime_nsec
as libc
::c_long
,
135 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
136 Ok(SystemTime
::from(libc
::timespec
{
137 tv_sec
: self.stat
.st_atime
as libc
::time_t
,
138 tv_nsec
: self.stat
.st_atime_nsec
as libc
::c_long
,
142 #[cfg(any(target_os = "bitrig",
143 target_os
= "freebsd",
144 target_os
= "openbsd",
147 pub fn created(&self) -> io
::Result
<SystemTime
> {
148 Ok(SystemTime
::from(libc
::timespec
{
149 tv_sec
: self.stat
.st_birthtime
as libc
::time_t
,
150 tv_nsec
: self.stat
.st_birthtime_nsec
as libc
::c_long
,
154 #[cfg(not(any(target_os = "bitrig",
155 target_os
= "freebsd",
156 target_os
= "openbsd",
158 target_os
= "ios")))]
159 pub fn created(&self) -> io
::Result
<SystemTime
> {
160 Err(io
::Error
::new(io
::ErrorKind
::Other
,
161 "creation time is not available on this platform \
166 impl AsInner
<stat64
> for FileAttr
{
167 fn as_inner(&self) -> &stat64 { &self.stat }
170 impl FilePermissions
{
171 pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 }
172 pub fn set_readonly(&mut self, readonly
: bool
) {
179 pub fn mode(&self) -> u32 { self.mode as u32 }
183 pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) }
184 pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) }
185 pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) }
187 pub fn is(&self, mode
: mode_t
) -> bool { self.mode & libc::S_IFMT == mode }
190 impl FromInner
<u32> for FilePermissions
{
191 fn from_inner(mode
: u32) -> FilePermissions
{
192 FilePermissions { mode: mode as mode_t }
196 impl Iterator
for ReadDir
{
197 type Item
= io
::Result
<DirEntry
>;
199 #[cfg(target_os = "solaris")]
200 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
203 // Although readdir_r(3) would be a correct function to use here because
204 // of the thread safety, on Illumos the readdir(3C) function is safe to use
205 // in threaded applications and it is generally preferred over the
206 // readdir_r(3C) function.
207 super::os
::set_errno(0);
208 let entry_ptr
= libc
::readdir(self.dirp
.0);
209 if entry_ptr
.is_null() {
210 // NULL can mean either the end is reached or an error occurred.
211 // So we had to clear errno beforehand to check for an error now.
212 return match super::os
::errno() {
214 e
=> Some(Err(Error
::from_raw_os_error(e
))),
218 let name
= (*entry_ptr
).d_name
.as_ptr();
219 let namelen
= libc
::strlen(name
) as usize;
223 name
: ::slice
::from_raw_parts(name
as *const u8,
224 namelen
as usize).to_owned().into_boxed_slice(),
225 root
: self.root
.clone()
227 if ret
.name_bytes() != b
"." && ret
.name_bytes() != b
".." {
234 #[cfg(not(target_os = "solaris"))]
235 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
237 let mut ret
= DirEntry
{
238 entry
: mem
::zeroed(),
239 root
: self.root
.clone()
241 let mut entry_ptr
= ptr
::null_mut();
243 if readdir64_r(self.dirp
.0, &mut ret
.entry
, &mut entry_ptr
) != 0 {
244 return Some(Err(Error
::last_os_error()))
246 if entry_ptr
.is_null() {
249 if ret
.name_bytes() != b
"." && ret
.name_bytes() != b
".." {
259 let r
= unsafe { libc::closedir(self.0) }
;
260 debug_assert_eq
!(r
, 0);
265 pub fn path(&self) -> PathBuf
{
266 self.root
.join(OsStr
::from_bytes(self.name_bytes()))
269 pub fn file_name(&self) -> OsString
{
270 OsStr
::from_bytes(self.name_bytes()).to_os_string()
273 pub fn metadata(&self) -> io
::Result
<FileAttr
> {
277 #[cfg(target_os = "solaris")]
278 pub fn file_type(&self) -> io
::Result
<FileType
> {
279 stat(&self.path()).map(|m
| m
.file_type())
282 #[cfg(target_os = "haiku")]
283 pub fn file_type(&self) -> io
::Result
<FileType
> {
284 lstat(&self.path()).map(|m
| m
.file_type())
287 #[cfg(not(any(target_os = "solaris", target_os = "haiku")))]
288 pub fn file_type(&self) -> io
::Result
<FileType
> {
289 match self.entry
.d_type
{
290 libc
::DT_CHR
=> Ok(FileType { mode: libc::S_IFCHR }
),
291 libc
::DT_FIFO
=> Ok(FileType { mode: libc::S_IFIFO }
),
292 libc
::DT_LNK
=> Ok(FileType { mode: libc::S_IFLNK }
),
293 libc
::DT_REG
=> Ok(FileType { mode: libc::S_IFREG }
),
294 libc
::DT_SOCK
=> Ok(FileType { mode: libc::S_IFSOCK }
),
295 libc
::DT_DIR
=> Ok(FileType { mode: libc::S_IFDIR }
),
296 libc
::DT_BLK
=> Ok(FileType { mode: libc::S_IFBLK }
),
297 _
=> lstat(&self.path()).map(|m
| m
.file_type()),
301 #[cfg(any(target_os = "macos",
304 target_os
= "emscripten",
305 target_os
= "android",
306 target_os
= "solaris",
307 target_os
= "haiku"))]
308 pub fn ino(&self) -> u64 {
309 self.entry
.d_ino
as u64
312 #[cfg(any(target_os = "freebsd",
313 target_os
= "openbsd",
314 target_os
= "bitrig",
315 target_os
= "netbsd",
316 target_os
= "dragonfly"))]
317 pub fn ino(&self) -> u64 {
318 self.entry
.d_fileno
as u64
321 #[cfg(any(target_os = "macos",
323 target_os
= "netbsd",
324 target_os
= "openbsd",
325 target_os
= "freebsd",
326 target_os
= "dragonfly",
327 target_os
= "bitrig"))]
328 fn name_bytes(&self) -> &[u8] {
330 ::slice
::from_raw_parts(self.entry
.d_name
.as_ptr() as *const u8,
331 self.entry
.d_namlen
as usize)
334 #[cfg(any(target_os = "android",
336 target_os
= "emscripten",
337 target_os
= "haiku"))]
338 fn name_bytes(&self) -> &[u8] {
340 CStr
::from_ptr(self.entry
.d_name
.as_ptr()).to_bytes()
343 #[cfg(target_os = "solaris")]
344 fn name_bytes(&self) -> &[u8] {
350 pub fn new() -> OpenOptions
{
365 pub fn read(&mut self, read
: bool
) { self.read = read; }
366 pub fn write(&mut self, write
: bool
) { self.write = write; }
367 pub fn append(&mut self, append
: bool
) { self.append = append; }
368 pub fn truncate(&mut self, truncate
: bool
) { self.truncate = truncate; }
369 pub fn create(&mut self, create
: bool
) { self.create = create; }
370 pub fn create_new(&mut self, create_new
: bool
) { self.create_new = create_new; }
372 pub fn custom_flags(&mut self, flags
: i32) { self.custom_flags = flags; }
373 pub fn mode(&mut self, mode
: u32) { self.mode = mode as mode_t; }
375 fn get_access_mode(&self) -> io
::Result
<c_int
> {
376 match (self.read
, self.write
, self.append
) {
377 (true, false, false) => Ok(libc
::O_RDONLY
),
378 (false, true, false) => Ok(libc
::O_WRONLY
),
379 (true, true, false) => Ok(libc
::O_RDWR
),
380 (false, _
, true) => Ok(libc
::O_WRONLY
| libc
::O_APPEND
),
381 (true, _
, true) => Ok(libc
::O_RDWR
| libc
::O_APPEND
),
382 (false, false, false) => Err(Error
::from_raw_os_error(libc
::EINVAL
)),
386 fn get_creation_mode(&self) -> io
::Result
<c_int
> {
387 match (self.write
, self.append
) {
390 if self.truncate
|| self.create
|| self.create_new
{
391 return Err(Error
::from_raw_os_error(libc
::EINVAL
));
394 if self.truncate
&& !self.create_new
{
395 return Err(Error
::from_raw_os_error(libc
::EINVAL
));
399 Ok(match (self.create
, self.truncate
, self.create_new
) {
400 (false, false, false) => 0,
401 (true, false, false) => libc
::O_CREAT
,
402 (false, true, false) => libc
::O_TRUNC
,
403 (true, true, false) => libc
::O_CREAT
| libc
::O_TRUNC
,
404 (_
, _
, true) => libc
::O_CREAT
| libc
::O_EXCL
,
410 pub fn open(path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
411 let path
= cstr(path
)?
;
412 File
::open_c(&path
, opts
)
415 pub fn open_c(path
: &CStr
, opts
: &OpenOptions
) -> io
::Result
<File
> {
416 let flags
= libc
::O_CLOEXEC
|
417 opts
.get_access_mode()?
|
418 opts
.get_creation_mode()?
|
419 (opts
.custom_flags
as c_int
& !libc
::O_ACCMODE
);
420 let fd
= cvt_r(|| unsafe {
421 open64(path
.as_ptr(), flags
, opts
.mode
as c_int
)
423 let fd
= FileDesc
::new(fd
);
425 // Currently the standard library supports Linux 2.6.18 which did not
426 // have the O_CLOEXEC flag (passed above). If we're running on an older
427 // Linux kernel then the flag is just ignored by the OS, so we continue
428 // to explicitly ask for a CLOEXEC fd here.
430 // The CLOEXEC flag, however, is supported on versions of OSX/BSD/etc
431 // that we support, so we only do this on Linux currently.
432 if cfg
!(target_os
= "linux") {
439 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
440 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
442 fstat64(self.0.raw(), &mut stat
)
444 Ok(FileAttr { stat: stat }
)
447 pub fn fsync(&self) -> io
::Result
<()> {
448 cvt_r(|| unsafe { libc::fsync(self.0.raw()) }
)?
;
452 pub fn datasync(&self) -> io
::Result
<()> {
453 cvt_r(|| unsafe { os_datasync(self.0.raw()) }
)?
;
456 #[cfg(any(target_os = "macos", target_os = "ios"))]
457 unsafe fn os_datasync(fd
: c_int
) -> c_int
{
458 libc
::fcntl(fd
, libc
::F_FULLFSYNC
)
460 #[cfg(target_os = "linux")]
461 unsafe fn os_datasync(fd
: c_int
) -> c_int { libc::fdatasync(fd) }
462 #[cfg(not(any(target_os = "macos",
464 target_os
= "linux")))]
465 unsafe fn os_datasync(fd
: c_int
) -> c_int { libc::fsync(fd) }
468 pub fn truncate(&self, size
: u64) -> io
::Result
<()> {
469 #[cfg(target_os = "android")]
470 return ::sys
::android
::ftruncate64(self.0.raw(), size
);
472 #[cfg(not(target_os = "android"))]
473 return cvt_r(|| unsafe {
474 ftruncate64(self.0.raw(), size
as off64_t
)
478 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
482 pub fn read_to_end(&self, buf
: &mut Vec
<u8>) -> io
::Result
<usize> {
483 self.0.read_to_end(buf
)
486 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
490 pub fn flush(&self) -> io
::Result
<()> { Ok(()) }
492 pub fn seek(&self, pos
: SeekFrom
) -> io
::Result
<u64> {
493 let (whence
, pos
) = match pos
{
494 // Casting to `i64` is fine, too large values will end up as
495 // negative which will cause an error in `lseek64`.
496 SeekFrom
::Start(off
) => (libc
::SEEK_SET
, off
as i64),
497 SeekFrom
::End(off
) => (libc
::SEEK_END
, off
),
498 SeekFrom
::Current(off
) => (libc
::SEEK_CUR
, off
),
500 let n
= cvt(unsafe { lseek64(self.0.raw(), pos, whence) }
)?
;
504 pub fn duplicate(&self) -> io
::Result
<File
> {
505 self.0.duplicate().map(File
)
508 pub fn fd(&self) -> &FileDesc { &self.0 }
510 pub fn into_fd(self) -> FileDesc { self.0 }
514 pub fn new() -> DirBuilder
{
515 DirBuilder { mode: 0o777 }
518 pub fn mkdir(&self, p
: &Path
) -> io
::Result
<()> {
520 cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }
)?
;
524 pub fn set_mode(&mut self, mode
: u32) {
525 self.mode
= mode
as mode_t
;
529 fn cstr(path
: &Path
) -> io
::Result
<CString
> {
530 Ok(CString
::new(path
.as_os_str().as_bytes())?
)
533 impl FromInner
<c_int
> for File
{
534 fn from_inner(fd
: c_int
) -> File
{
535 File(FileDesc
::new(fd
))
539 impl fmt
::Debug
for File
{
540 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
541 #[cfg(target_os = "linux")]
542 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
543 let mut p
= PathBuf
::from("/proc/self/fd");
544 p
.push(&fd
.to_string());
548 #[cfg(target_os = "macos")]
549 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
550 // FIXME: The use of PATH_MAX is generally not encouraged, but it
551 // is inevitable in this case because OS X defines `fcntl` with
552 // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
553 // alternatives. If a better method is invented, it should be used
555 let mut buf
= vec
![0;libc
::PATH_MAX
as usize];
556 let n
= unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }
;
560 let l
= buf
.iter().position(|&c
| c
== 0).unwrap();
561 buf
.truncate(l
as usize);
563 Some(PathBuf
::from(OsString
::from_vec(buf
)))
566 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
567 fn get_path(_fd
: c_int
) -> Option
<PathBuf
> {
568 // FIXME(#24570): implement this for other Unix platforms
572 #[cfg(any(target_os = "linux", target_os = "macos"))]
573 fn get_mode(fd
: c_int
) -> Option
<(bool
, bool
)> {
574 let mode
= unsafe { libc::fcntl(fd, libc::F_GETFL) }
;
578 match mode
& libc
::O_ACCMODE
{
579 libc
::O_RDONLY
=> Some((true, false)),
580 libc
::O_RDWR
=> Some((true, true)),
581 libc
::O_WRONLY
=> Some((false, true)),
586 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
587 fn get_mode(_fd
: c_int
) -> Option
<(bool
, bool
)> {
588 // FIXME(#24570): implement this for other Unix platforms
592 let fd
= self.0.raw();
593 let mut b
= f
.debug_struct("File");
595 if let Some(path
) = get_path(fd
) {
596 b
.field("path", &path
);
598 if let Some((read
, write
)) = get_mode(fd
) {
599 b
.field("read", &read
).field("write", &write
);
605 pub fn readdir(p
: &Path
) -> io
::Result
<ReadDir
> {
606 let root
= Arc
::new(p
.to_path_buf());
609 let ptr
= libc
::opendir(p
.as_ptr());
611 Err(Error
::last_os_error())
613 Ok(ReadDir { dirp: Dir(ptr), root: root }
)
618 pub fn unlink(p
: &Path
) -> io
::Result
<()> {
620 cvt(unsafe { libc::unlink(p.as_ptr()) }
)?
;
624 pub fn rename(old
: &Path
, new
: &Path
) -> io
::Result
<()> {
625 let old
= cstr(old
)?
;
626 let new
= cstr(new
)?
;
627 cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }
)?
;
631 pub fn set_perm(p
: &Path
, perm
: FilePermissions
) -> io
::Result
<()> {
633 cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }
)?
;
637 pub fn rmdir(p
: &Path
) -> io
::Result
<()> {
639 cvt(unsafe { libc::rmdir(p.as_ptr()) }
)?
;
643 pub fn remove_dir_all(path
: &Path
) -> io
::Result
<()> {
644 let filetype
= lstat(path
)?
.file_type();
645 if filetype
.is_symlink() {
648 remove_dir_all_recursive(path
)
652 fn remove_dir_all_recursive(path
: &Path
) -> io
::Result
<()> {
653 for child
in readdir(path
)?
{
655 if child
.file_type()?
.is_dir() {
656 remove_dir_all_recursive(&child
.path())?
;
658 unlink(&child
.path())?
;
664 pub fn readlink(p
: &Path
) -> io
::Result
<PathBuf
> {
665 let c_path
= cstr(p
)?
;
666 let p
= c_path
.as_ptr();
668 let mut buf
= Vec
::with_capacity(256);
671 let buf_read
= cvt(unsafe {
672 libc
::readlink(p
, buf
.as_mut_ptr() as *mut _
, buf
.capacity() as libc
::size_t
)
675 unsafe { buf.set_len(buf_read); }
677 if buf_read
!= buf
.capacity() {
680 return Ok(PathBuf
::from(OsString
::from_vec(buf
)));
683 // Trigger the internal buffer resizing logic of `Vec` by requiring
684 // more space than the current capacity. The length is guaranteed to be
685 // the same as the capacity due to the if statement above.
690 pub fn symlink(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
691 let src
= cstr(src
)?
;
692 let dst
= cstr(dst
)?
;
693 cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) }
)?
;
697 pub fn link(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
698 let src
= cstr(src
)?
;
699 let dst
= cstr(dst
)?
;
700 cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) }
)?
;
704 pub fn stat(p
: &Path
) -> io
::Result
<FileAttr
> {
706 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
708 stat64(p
.as_ptr(), &mut stat
as *mut _
as *mut _
)
710 Ok(FileAttr { stat: stat }
)
713 pub fn lstat(p
: &Path
) -> io
::Result
<FileAttr
> {
715 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
717 lstat64(p
.as_ptr(), &mut stat
as *mut _
as *mut _
)
719 Ok(FileAttr { stat: stat }
)
722 pub fn canonicalize(p
: &Path
) -> io
::Result
<PathBuf
> {
723 let path
= CString
::new(p
.as_os_str().as_bytes())?
;
726 let r
= libc
::realpath(path
.as_ptr(), ptr
::null_mut());
728 return Err(io
::Error
::last_os_error())
730 buf
= CStr
::from_ptr(r
).to_bytes().to_vec();
731 libc
::free(r
as *mut _
);
733 Ok(PathBuf
::from(OsString
::from_vec(buf
)))
736 pub fn copy(from
: &Path
, to
: &Path
) -> io
::Result
<u64> {
737 use fs
::{File, set_permissions}
;
739 return Err(Error
::new(ErrorKind
::InvalidInput
,
740 "the source path is not an existing regular file"))
743 let mut reader
= File
::open(from
)?
;
744 let mut writer
= File
::create(to
)?
;
745 let perm
= reader
.metadata()?
.permissions();
747 let ret
= io
::copy(&mut reader
, &mut writer
)?
;
748 set_permissions(to
, perm
)?
;