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.
13 use os
::unix
::prelude
::*;
15 use ffi
::{CString, CStr, OsString, OsStr}
;
17 use io
::{self, Error, ErrorKind, SeekFrom}
;
18 use libc
::{self, c_int, size_t, off_t, c_char, mode_t}
;
20 use path
::{Path, PathBuf}
;
23 use sys
::fd
::FileDesc
;
24 use sys
::platform
::raw
;
25 use sys
::{c, cvt, cvt_r}
;
26 use sys_common
::{AsInner, FromInner}
;
29 pub struct File(FileDesc
);
40 struct Dir(*mut libc
::DIR
);
42 unsafe impl Send
for Dir {}
43 unsafe impl Sync
for Dir {}
46 buf
: Vec
<u8>, // actually *mut libc::dirent_t
51 pub struct OpenOptions
{
58 #[derive(Clone, PartialEq, Eq, Debug)]
59 pub struct FilePermissions { mode: mode_t }
61 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
62 pub struct FileType { mode: mode_t }
64 pub struct DirBuilder { mode: mode_t }
67 pub fn size(&self) -> u64 { self.stat.st_size as u64 }
68 pub fn perm(&self) -> FilePermissions
{
69 FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 }
72 pub fn file_type(&self) -> FileType
{
73 FileType { mode: self.stat.st_mode as mode_t }
77 impl AsInner
<raw
::stat
> for FileAttr
{
78 fn as_inner(&self) -> &raw
::stat { &self.stat }
81 /// OS-specific extension methods for `fs::Metadata`
82 #[stable(feature = "metadata_ext", since = "1.1.0")]
83 pub trait MetadataExt
{
84 /// Gain a reference to the underlying `stat` structure which contains the
85 /// raw information returned by the OS.
87 /// The contents of the returned `stat` are **not** consistent across Unix
88 /// platforms. The `os::unix::fs::MetadataExt` trait contains the cross-Unix
89 /// abstractions contained within the raw stat.
90 #[stable(feature = "metadata_ext", since = "1.1.0")]
91 fn as_raw_stat(&self) -> &raw
::stat
;
94 #[stable(feature = "metadata_ext", since = "1.1.0")]
95 impl MetadataExt
for ::fs
::Metadata
{
96 fn as_raw_stat(&self) -> &raw
::stat { &self.as_inner().stat }
99 impl FilePermissions
{
100 pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 }
101 pub fn set_readonly(&mut self, readonly
: bool
) {
108 pub fn mode(&self) -> raw
::mode_t { self.mode }
112 pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) }
113 pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) }
114 pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) }
116 pub fn is(&self, mode
: mode_t
) -> bool { self.mode & libc::S_IFMT == mode }
119 impl FromInner
<raw
::mode_t
> for FilePermissions
{
120 fn from_inner(mode
: raw
::mode_t
) -> FilePermissions
{
121 FilePermissions { mode: mode as mode_t }
125 impl Iterator
for ReadDir
{
126 type Item
= io
::Result
<DirEntry
>;
128 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
130 fn rust_dirent_t_size() -> c_int
;
133 let mut buf
: Vec
<u8> = Vec
::with_capacity(unsafe {
134 rust_dirent_t_size() as usize
136 let ptr
= buf
.as_mut_ptr() as *mut libc
::dirent_t
;
138 let mut entry_ptr
= ptr
::null_mut();
140 if unsafe { libc::readdir_r(self.dirp.0, ptr, &mut entry_ptr) != 0 }
{
141 return Some(Err(Error
::last_os_error()))
143 if entry_ptr
.is_null() {
147 let entry
= DirEntry
{
149 root
: self.root
.clone()
151 if entry
.name_bytes() == b
"." || entry
.name_bytes() == b
".." {
154 return Some(Ok(entry
))
162 let r
= unsafe { libc::closedir(self.0) }
;
163 debug_assert_eq
!(r
, 0);
168 pub fn path(&self) -> PathBuf
{
169 self.root
.join(<OsStr
as OsStrExt
>::from_bytes(self.name_bytes()))
172 pub fn file_name(&self) -> OsString
{
173 OsStr
::from_bytes(self.name_bytes()).to_os_string()
176 pub fn metadata(&self) -> io
::Result
<FileAttr
> {
180 pub fn file_type(&self) -> io
::Result
<FileType
> {
182 fn rust_dir_get_mode(ptr
: *mut libc
::dirent_t
) -> c_int
;
185 match rust_dir_get_mode(self.dirent()) {
186 -1 => lstat(&self.path()).map(|m
| m
.file_type()),
187 n
=> Ok(FileType { mode: n as mode_t }
),
192 pub fn ino(&self) -> raw
::ino_t
{
194 fn rust_dir_get_ino(ptr
: *mut libc
::dirent_t
) -> raw
::ino_t
;
196 unsafe { rust_dir_get_ino(self.dirent()) }
199 fn name_bytes(&self) -> &[u8] {
201 fn rust_list_dir_val(ptr
: *mut libc
::dirent_t
) -> *const c_char
;
204 CStr
::from_ptr(rust_list_dir_val(self.dirent())).to_bytes()
208 fn dirent(&self) -> *mut libc
::dirent_t
{
209 self.buf
.as_ptr() as *mut _
214 pub fn new() -> OpenOptions
{
223 pub fn read(&mut self, read
: bool
) {
227 pub fn write(&mut self, write
: bool
) {
231 pub fn append(&mut self, append
: bool
) {
232 self.flag(libc
::O_APPEND
, append
);
235 pub fn truncate(&mut self, truncate
: bool
) {
236 self.flag(libc
::O_TRUNC
, truncate
);
239 pub fn create(&mut self, create
: bool
) {
240 self.flag(libc
::O_CREAT
, create
);
243 pub fn mode(&mut self, mode
: raw
::mode_t
) {
244 self.mode
= mode
as mode_t
;
247 fn flag(&mut self, bit
: c_int
, on
: bool
) {
257 pub fn open(path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
258 let path
= try
!(cstr(path
));
259 File
::open_c(&path
, opts
)
262 pub fn open_c(path
: &CStr
, opts
: &OpenOptions
) -> io
::Result
<File
> {
263 let flags
= opts
.flags
| match (opts
.read
, opts
.write
) {
264 (true, true) => libc
::O_RDWR
,
265 (false, true) => libc
::O_WRONLY
,
267 (false, false) => libc
::O_RDONLY
,
269 let fd
= try
!(cvt_r(|| unsafe {
270 libc
::open(path
.as_ptr(), flags
, opts
.mode
)
272 let fd
= FileDesc
::new(fd
);
277 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
278 let mut stat
: raw
::stat
= unsafe { mem::zeroed() }
;
280 libc
::fstat(self.0.raw(), &mut stat
as *mut _
as *mut _
)
282 Ok(FileAttr { stat: stat }
)
285 pub fn fsync(&self) -> io
::Result
<()> {
286 try
!(cvt_r(|| unsafe { libc::fsync(self.0.raw()) }
));
290 pub fn datasync(&self) -> io
::Result
<()> {
291 try
!(cvt_r(|| unsafe { os_datasync(self.0.raw()) }
));
294 #[cfg(any(target_os = "macos", target_os = "ios"))]
295 unsafe fn os_datasync(fd
: c_int
) -> c_int
{
296 libc
::fcntl(fd
, libc
::F_FULLFSYNC
)
298 #[cfg(target_os = "linux")]
299 unsafe fn os_datasync(fd
: c_int
) -> c_int { libc::fdatasync(fd) }
300 #[cfg(not(any(target_os = "macos",
302 target_os
= "linux")))]
303 unsafe fn os_datasync(fd
: c_int
) -> c_int { libc::fsync(fd) }
306 pub fn truncate(&self, size
: u64) -> io
::Result
<()> {
307 try
!(cvt_r(|| unsafe {
308 libc
::ftruncate(self.0.raw(), size
as libc
::off_t
)
313 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
317 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
321 pub fn flush(&self) -> io
::Result
<()> { Ok(()) }
323 pub fn seek(&self, pos
: SeekFrom
) -> io
::Result
<u64> {
324 let (whence
, pos
) = match pos
{
325 SeekFrom
::Start(off
) => (libc
::SEEK_SET
, off
as off_t
),
326 SeekFrom
::End(off
) => (libc
::SEEK_END
, off
as off_t
),
327 SeekFrom
::Current(off
) => (libc
::SEEK_CUR
, off
as off_t
),
329 let n
= try
!(cvt(unsafe { libc::lseek(self.0.raw(), pos, whence) }
));
333 pub fn fd(&self) -> &FileDesc { &self.0 }
335 pub fn into_fd(self) -> FileDesc { self.0 }
339 pub fn new() -> DirBuilder
{
340 DirBuilder { mode: 0o777 }
343 pub fn mkdir(&self, p
: &Path
) -> io
::Result
<()> {
344 let p
= try
!(cstr(p
));
345 try
!(cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }
));
349 pub fn set_mode(&mut self, mode
: mode_t
) {
354 fn cstr(path
: &Path
) -> io
::Result
<CString
> {
355 path
.as_os_str().to_cstring().ok_or(
356 io
::Error
::new(io
::ErrorKind
::InvalidInput
, "path contained a null"))
359 impl FromInner
<c_int
> for File
{
360 fn from_inner(fd
: c_int
) -> File
{
361 File(FileDesc
::new(fd
))
365 impl fmt
::Debug
for File
{
366 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
367 #[cfg(target_os = "linux")]
368 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
369 use string
::ToString
;
370 let mut p
= PathBuf
::from("/proc/self/fd");
371 p
.push(&fd
.to_string());
375 #[cfg(target_os = "macos")]
376 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
377 let mut buf
= vec
![0;libc
::PATH_MAX
as usize];
378 let n
= unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }
;
382 let l
= buf
.iter().position(|&c
| c
== 0).unwrap();
383 buf
.truncate(l
as usize);
384 Some(PathBuf
::from(OsString
::from_vec(buf
)))
387 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
388 fn get_path(_fd
: c_int
) -> Option
<PathBuf
> {
389 // FIXME(#24570): implement this for other Unix platforms
393 #[cfg(any(target_os = "linux", target_os = "macos"))]
394 fn get_mode(fd
: c_int
) -> Option
<(bool
, bool
)> {
395 let mode
= unsafe { libc::fcntl(fd, libc::F_GETFL) }
;
399 match mode
& libc
::O_ACCMODE
{
400 libc
::O_RDONLY
=> Some((true, false)),
401 libc
::O_RDWR
=> Some((true, true)),
402 libc
::O_WRONLY
=> Some((false, true)),
407 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
408 fn get_mode(_fd
: c_int
) -> Option
<(bool
, bool
)> {
409 // FIXME(#24570): implement this for other Unix platforms
413 let fd
= self.0.raw();
414 let mut b
= f
.debug_struct("File");
416 if let Some(path
) = get_path(fd
) {
417 b
.field("path", &path
);
419 if let Some((read
, write
)) = get_mode(fd
) {
420 b
.field("read", &read
).field("write", &write
);
426 pub fn readdir(p
: &Path
) -> io
::Result
<ReadDir
> {
427 let root
= Arc
::new(p
.to_path_buf());
428 let p
= try
!(cstr(p
));
430 let ptr
= libc
::opendir(p
.as_ptr());
432 Err(Error
::last_os_error())
434 Ok(ReadDir { dirp: Dir(ptr), root: root }
)
439 pub fn unlink(p
: &Path
) -> io
::Result
<()> {
440 let p
= try
!(cstr(p
));
441 try
!(cvt(unsafe { libc::unlink(p.as_ptr()) }
));
445 pub fn rename(old
: &Path
, new
: &Path
) -> io
::Result
<()> {
446 let old
= try
!(cstr(old
));
447 let new
= try
!(cstr(new
));
448 try
!(cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }
));
452 pub fn set_perm(p
: &Path
, perm
: FilePermissions
) -> io
::Result
<()> {
453 let p
= try
!(cstr(p
));
454 try
!(cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }
));
458 pub fn rmdir(p
: &Path
) -> io
::Result
<()> {
459 let p
= try
!(cstr(p
));
460 try
!(cvt(unsafe { libc::rmdir(p.as_ptr()) }
));
464 pub fn readlink(p
: &Path
) -> io
::Result
<PathBuf
> {
465 let c_path
= try
!(cstr(p
));
466 let p
= c_path
.as_ptr();
467 let mut len
= unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) }
;
469 len
= 1024; // FIXME: read PATH_MAX from C ffi?
471 let mut buf
: Vec
<u8> = Vec
::with_capacity(len
as usize);
474 libc
::readlink(p
, buf
.as_ptr() as *mut c_char
, len
as size_t
)
476 buf
.set_len(n
as usize);
478 Ok(PathBuf
::from(OsString
::from_vec(buf
)))
481 pub fn symlink(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
482 let src
= try
!(cstr(src
));
483 let dst
= try
!(cstr(dst
));
484 try
!(cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) }
));
488 pub fn link(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
489 let src
= try
!(cstr(src
));
490 let dst
= try
!(cstr(dst
));
491 try
!(cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) }
));
495 pub fn stat(p
: &Path
) -> io
::Result
<FileAttr
> {
496 let p
= try
!(cstr(p
));
497 let mut stat
: raw
::stat
= unsafe { mem::zeroed() }
;
499 libc
::stat(p
.as_ptr(), &mut stat
as *mut _
as *mut _
)
501 Ok(FileAttr { stat: stat }
)
504 pub fn lstat(p
: &Path
) -> io
::Result
<FileAttr
> {
505 let p
= try
!(cstr(p
));
506 let mut stat
: raw
::stat
= unsafe { mem::zeroed() }
;
508 libc
::lstat(p
.as_ptr(), &mut stat
as *mut _
as *mut _
)
510 Ok(FileAttr { stat: stat }
)
513 pub fn utimes(p
: &Path
, atime
: u64, mtime
: u64) -> io
::Result
<()> {
514 let p
= try
!(cstr(p
));
515 let buf
= [super::ms_to_timeval(atime
), super::ms_to_timeval(mtime
)];
516 try
!(cvt(unsafe { c::utimes(p.as_ptr(), buf.as_ptr()) }
));
520 pub fn canonicalize(p
: &Path
) -> io
::Result
<PathBuf
> {
521 let path
= try
!(CString
::new(p
.as_os_str().as_bytes()));
522 let mut buf
= vec
![0u8; 16 * 1024];
524 let r
= c
::realpath(path
.as_ptr(), buf
.as_mut_ptr() as *mut _
);
526 return Err(io
::Error
::last_os_error())
529 let p
= buf
.iter().position(|i
| *i
== 0).unwrap();
531 Ok(PathBuf
::from(OsString
::from_vec(buf
)))
534 pub fn copy(from
: &Path
, to
: &Path
) -> io
::Result
<u64> {
535 use fs
::{File, PathExt, set_permissions}
;
537 return Err(Error
::new(ErrorKind
::InvalidInput
,
538 "the source path is not an existing file"))
541 let mut reader
= try
!(File
::open(from
));
542 let mut writer
= try
!(File
::create(to
));
543 let perm
= try
!(reader
.metadata()).permissions();
545 let ret
= try
!(io
::copy(&mut reader
, &mut writer
));
546 try
!(set_permissions(to
, perm
));