diff --git a/src/libnative/io/file_unix.rs b/src/libnative/io/file_unix.rs deleted file mode 100644 index f616295c73d1..000000000000 --- a/src/libnative/io/file_unix.rs +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Blocking posix-based file I/O - -use alloc::arc::Arc; -use libc::{mod, c_int, c_void}; -use std::c_str::CString; -use std::mem; -use std::rt::rtio::{mod, IoResult}; - -use io::{retry, keep_going}; -use io::util; - -pub type fd_t = libc::c_int; - -struct Inner { - fd: fd_t, - close_on_drop: bool, -} - -pub struct FileDesc { - inner: Arc -} - -impl FileDesc { - /// Create a `FileDesc` from an open C file descriptor. - /// - /// The `FileDesc` will take ownership of the specified file descriptor and - /// close it upon destruction if the `close_on_drop` flag is true, otherwise - /// it will not close the file descriptor when this `FileDesc` is dropped. - /// - /// Note that all I/O operations done on this object will be *blocking*, but - /// they do not require the runtime to be active. - pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc { - FileDesc { inner: Arc::new(Inner { - fd: fd, - close_on_drop: close_on_drop - }) } - } - - // FIXME(#10465) these functions should not be public, but anything in - // native::io wanting to use them is forced to have all the - // rtio traits in scope - pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult { - let ret = retry(|| unsafe { - libc::read(self.fd(), - buf.as_mut_ptr() as *mut libc::c_void, - buf.len() as libc::size_t) - }); - if ret == 0 { - Err(util::eof()) - } else if ret < 0 { - Err(super::last_error()) - } else { - Ok(ret as uint) - } - } - pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> { - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::write(self.fd(), buf as *const libc::c_void, - len as libc::size_t) as i64 - } - }); - if ret < 0 { - Err(super::last_error()) - } else { - Ok(()) - } - } - - pub fn fd(&self) -> fd_t { self.inner.fd } -} - -impl rtio::RtioFileStream for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner_read(buf).map(|i| i as int) - } - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner_write(buf) - } - fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult { - match retry(|| unsafe { - libc::pread(self.fd(), buf.as_ptr() as *mut _, - buf.len() as libc::size_t, - offset as libc::off_t) - }) { - -1 => Err(super::last_error()), - n => Ok(n as int) - } - } - fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> { - super::mkerr_libc(retry(|| unsafe { - libc::pwrite(self.fd(), buf.as_ptr() as *const _, - buf.len() as libc::size_t, offset as libc::off_t) - })) - } - fn seek(&mut self, pos: i64, whence: rtio::SeekStyle) -> IoResult { - let whence = match whence { - rtio::SeekSet => libc::SEEK_SET, - rtio::SeekEnd => libc::SEEK_END, - rtio::SeekCur => libc::SEEK_CUR, - }; - let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) }; - if n < 0 { - Err(super::last_error()) - } else { - Ok(n as u64) - } - } - fn tell(&self) -> IoResult { - let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) }; - if n < 0 { - Err(super::last_error()) - } else { - Ok(n as u64) - } - } - fn fsync(&mut self) -> IoResult<()> { - super::mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) })) - } - fn datasync(&mut self) -> IoResult<()> { - return super::mkerr_libc(os_datasync(self.fd())); - - #[cfg(any(target_os = "macos", target_os = "ios"))] - fn os_datasync(fd: c_int) -> c_int { - unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) } - } - #[cfg(target_os = "linux")] - fn os_datasync(fd: c_int) -> c_int { - retry(|| unsafe { libc::fdatasync(fd) }) - } - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))] - fn os_datasync(fd: c_int) -> c_int { - retry(|| unsafe { libc::fsync(fd) }) - } - } - fn truncate(&mut self, offset: i64) -> IoResult<()> { - super::mkerr_libc(retry(|| unsafe { - libc::ftruncate(self.fd(), offset as libc::off_t) - })) - } - - fn fstat(&mut self) -> IoResult { - let mut stat: libc::stat = unsafe { mem::zeroed() }; - match unsafe { libc::fstat(self.fd(), &mut stat) } { - 0 => Ok(mkstat(&stat)), - _ => Err(super::last_error()), - } - } -} - -impl rtio::RtioPipe for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner_read(buf) - } - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner_write(buf) - } - fn clone(&self) -> Box { - box FileDesc { inner: self.inner.clone() } as Box - } - - // Only supported on named pipes currently. Note that this doesn't have an - // impact on the std::io primitives, this is never called via - // std::io::PipeStream. If the functionality is exposed in the future, then - // these methods will need to be implemented. - fn close_read(&mut self) -> IoResult<()> { - Err(super::unimpl()) - } - fn close_write(&mut self) -> IoResult<()> { - Err(super::unimpl()) - } - fn set_timeout(&mut self, _t: Option) {} - fn set_read_timeout(&mut self, _t: Option) {} - fn set_write_timeout(&mut self, _t: Option) {} -} - -impl rtio::RtioTTY for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner_read(buf) - } - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner_write(buf) - } - fn set_raw(&mut self, _raw: bool) -> IoResult<()> { - Err(super::unimpl()) - } - fn get_winsize(&mut self) -> IoResult<(int, int)> { - Err(super::unimpl()) - } - fn isatty(&self) -> bool { false } -} - -impl Drop for Inner { - fn drop(&mut self) { - // closing stdio file handles makes no sense, so never do it. Also, note - // that errors are ignored when closing a file descriptor. The reason - // for this is that if an error occurs we don't actually know if the - // file descriptor was closed or not, and if we retried (for something - // like EINTR), we might close another valid file descriptor (opened - // after we closed ours. - if self.close_on_drop && self.fd > libc::STDERR_FILENO { - let n = unsafe { libc::close(self.fd) }; - if n != 0 { - println!("error {} when closing file descriptor {}", n, - self.fd); - } - } - } -} - -pub struct CFile { - file: *mut libc::FILE, - fd: FileDesc, -} - -impl CFile { - /// Create a `CFile` from an open `FILE` pointer. - /// - /// The `CFile` takes ownership of the `FILE` pointer and will close it upon - /// destruction. - pub fn new(file: *mut libc::FILE) -> CFile { - CFile { - file: file, - fd: FileDesc::new(unsafe { libc::fileno(file) }, false) - } - } - - pub fn flush(&mut self) -> IoResult<()> { - super::mkerr_libc(retry(|| unsafe { libc::fflush(self.file) })) - } -} - -impl rtio::RtioFileStream for CFile { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t, - self.file) as i64 - } - }); - if ret == 0 { - Err(util::eof()) - } else if ret < 0 { - Err(super::last_error()) - } else { - Ok(ret as int) - } - } - - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::fwrite(buf as *const libc::c_void, 1, len as libc::size_t, - self.file) as i64 - } - }); - if ret < 0 { - Err(super::last_error()) - } else { - Ok(()) - } - } - - fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult { - self.flush().and_then(|()| self.fd.pread(buf, offset)) - } - fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> { - self.flush().and_then(|()| self.fd.pwrite(buf, offset)) - } - fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult { - let whence = match style { - rtio::SeekSet => libc::SEEK_SET, - rtio::SeekEnd => libc::SEEK_END, - rtio::SeekCur => libc::SEEK_CUR, - }; - let n = unsafe { libc::fseek(self.file, pos as libc::c_long, whence) }; - if n < 0 { - Err(super::last_error()) - } else { - Ok(n as u64) - } - } - fn tell(&self) -> IoResult { - let ret = unsafe { libc::ftell(self.file) }; - if ret < 0 { - Err(super::last_error()) - } else { - Ok(ret as u64) - } - } - fn fsync(&mut self) -> IoResult<()> { - self.flush().and_then(|()| self.fd.fsync()) - } - fn datasync(&mut self) -> IoResult<()> { - self.flush().and_then(|()| self.fd.datasync()) - } - fn truncate(&mut self, offset: i64) -> IoResult<()> { - self.flush().and_then(|()| self.fd.truncate(offset)) - } - - fn fstat(&mut self) -> IoResult { - self.flush().and_then(|()| self.fd.fstat()) - } -} - -impl Drop for CFile { - fn drop(&mut self) { - unsafe { let _ = libc::fclose(self.file); } - } -} - -pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess) - -> IoResult -{ - let flags = match fm { - rtio::Open => 0, - rtio::Append => libc::O_APPEND, - rtio::Truncate => libc::O_TRUNC, - }; - // Opening with a write permission must silently create the file. - let (flags, mode) = match fa { - rtio::Read => (flags | libc::O_RDONLY, 0), - rtio::Write => (flags | libc::O_WRONLY | libc::O_CREAT, - libc::S_IRUSR | libc::S_IWUSR), - rtio::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT, - libc::S_IRUSR | libc::S_IWUSR), - }; - - match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) { - -1 => Err(super::last_error()), - fd => Ok(FileDesc::new(fd, true)), - } -} - -pub fn mkdir(p: &CString, mode: uint) -> IoResult<()> { - super::mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) }) -} - -pub fn readdir(p: &CString) -> IoResult> { - use libc::{dirent_t}; - use libc::{opendir, readdir_r, closedir}; - - fn prune(root: &CString, dirs: Vec) -> Vec { - let root = unsafe { CString::new(root.as_ptr(), false) }; - let root = Path::new(root); - - dirs.into_iter().filter(|path| { - path.as_vec() != b"." && path.as_vec() != b".." - }).map(|path| root.join(path).to_c_str()).collect() - } - - extern { - fn rust_dirent_t_size() -> libc::c_int; - fn rust_list_dir_val(ptr: *mut dirent_t) -> *const libc::c_char; - } - - let size = unsafe { rust_dirent_t_size() }; - let mut buf = Vec::::with_capacity(size as uint); - let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t; - - let dir_ptr = unsafe {opendir(p.as_ptr())}; - - if dir_ptr as uint != 0 { - let mut paths = vec!(); - let mut entry_ptr = 0 as *mut dirent_t; - while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } { - if entry_ptr.is_null() { break } - let cstr = unsafe { - CString::new(rust_list_dir_val(entry_ptr), false) - }; - paths.push(Path::new(cstr)); - } - assert_eq!(unsafe { closedir(dir_ptr) }, 0); - Ok(prune(p, paths)) - } else { - Err(super::last_error()) - } -} - -pub fn unlink(p: &CString) -> IoResult<()> { - super::mkerr_libc(unsafe { libc::unlink(p.as_ptr()) }) -} - -pub fn rename(old: &CString, new: &CString) -> IoResult<()> { - super::mkerr_libc(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }) -} - -pub fn chmod(p: &CString, mode: uint) -> IoResult<()> { - super::mkerr_libc(retry(|| unsafe { - libc::chmod(p.as_ptr(), mode as libc::mode_t) - })) -} - -pub fn rmdir(p: &CString) -> IoResult<()> { - super::mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) }) -} - -pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> { - super::mkerr_libc(retry(|| unsafe { - libc::chown(p.as_ptr(), uid as libc::uid_t, - gid as libc::gid_t) - })) -} - -pub fn readlink(p: &CString) -> IoResult { - let p = p.as_ptr(); - let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) }; - if len == -1 { - len = 1024; // FIXME: read PATH_MAX from C ffi? - } - let mut buf: Vec = Vec::with_capacity(len as uint); - match unsafe { - libc::readlink(p, buf.as_ptr() as *mut libc::c_char, - len as libc::size_t) as libc::c_int - } { - -1 => Err(super::last_error()), - n => { - assert!(n > 0); - unsafe { buf.set_len(n as uint); } - Ok(buf.as_slice().to_c_str()) - } - } -} - -pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> { - super::mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) }) -} - -pub fn link(src: &CString, dst: &CString) -> IoResult<()> { - super::mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) }) -} - -fn mkstat(stat: &libc::stat) -> rtio::FileStat { - // FileStat times are in milliseconds - fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } - - #[cfg(not(any(target_os = "linux", target_os = "android")))] - fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 } - #[cfg(any(target_os = "linux", target_os = "android"))] - fn flags(_stat: &libc::stat) -> u64 { 0 } - - #[cfg(not(any(target_os = "linux", target_os = "android")))] - fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 } - #[cfg(any(target_os = "linux", target_os = "android"))] - fn gen(_stat: &libc::stat) -> u64 { 0 } - - rtio::FileStat { - size: stat.st_size as u64, - kind: stat.st_mode as u64, - perm: stat.st_mode as u64, - created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64), - modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64), - accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64), - device: stat.st_dev as u64, - inode: stat.st_ino as u64, - rdev: stat.st_rdev as u64, - nlink: stat.st_nlink as u64, - uid: stat.st_uid as u64, - gid: stat.st_gid as u64, - blksize: stat.st_blksize as u64, - blocks: stat.st_blocks as u64, - flags: flags(stat), - gen: gen(stat), - } -} - -pub fn stat(p: &CString) -> IoResult { - let mut stat: libc::stat = unsafe { mem::zeroed() }; - match unsafe { libc::stat(p.as_ptr(), &mut stat) } { - 0 => Ok(mkstat(&stat)), - _ => Err(super::last_error()), - } -} - -pub fn lstat(p: &CString) -> IoResult { - let mut stat: libc::stat = unsafe { mem::zeroed() }; - match unsafe { libc::lstat(p.as_ptr(), &mut stat) } { - 0 => Ok(mkstat(&stat)), - _ => Err(super::last_error()), - } -} - -pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> { - let buf = libc::utimbuf { - actime: (atime / 1000) as libc::time_t, - modtime: (mtime / 1000) as libc::time_t, - }; - super::mkerr_libc(unsafe { libc::utime(p.as_ptr(), &buf) }) -} - -#[cfg(test)] -mod tests { - use super::{CFile, FileDesc}; - use libc; - use std::os; - use std::rt::rtio::{RtioFileStream, SeekSet}; - - #[cfg_attr(target_os = "freebsd", ignore)] // hmm, maybe pipes have a tiny buffer - #[test] - fn test_file_desc() { - // Run this test with some pipes so we don't have to mess around with - // opening or closing files. - let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() }; - let mut reader = FileDesc::new(reader, true); - let mut writer = FileDesc::new(writer, true); - - writer.inner_write(b"test").ok().unwrap(); - let mut buf = [0u8, ..4]; - match reader.inner_read(buf) { - Ok(4) => { - assert_eq!(buf[0], 't' as u8); - assert_eq!(buf[1], 'e' as u8); - assert_eq!(buf[2], 's' as u8); - assert_eq!(buf[3], 't' as u8); - } - r => panic!("invalid read: {}", r), - } - - assert!(writer.inner_read(buf).is_err()); - assert!(reader.inner_write(buf).is_err()); - } - - #[test] - fn test_cfile() { - unsafe { - let f = libc::tmpfile(); - assert!(!f.is_null()); - let mut file = CFile::new(f); - - file.write(b"test").ok().unwrap(); - let mut buf = [0u8, ..4]; - let _ = file.seek(0, SeekSet).ok().unwrap(); - match file.read(buf) { - Ok(4) => { - assert_eq!(buf[0], 't' as u8); - assert_eq!(buf[1], 'e' as u8); - assert_eq!(buf[2], 's' as u8); - assert_eq!(buf[3], 't' as u8); - } - r => panic!("invalid read: {}", r) - } - } - } -} diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index a541712e17f7..baf58b83dcd2 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -30,7 +30,6 @@ use std::rt::rtio::{mod, IoResult, IoError}; use std::num; // Local re-exports -pub use self::file::FileDesc; pub use self::process::Process; mod helper_thread; @@ -41,13 +40,6 @@ pub mod net; pub mod process; mod util; -#[cfg(unix)] -#[path = "file_unix.rs"] -pub mod file; -#[cfg(windows)] -#[path = "file_windows.rs"] -pub mod file; - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", @@ -92,25 +84,6 @@ fn last_error() -> IoError { } } -// unix has nonzero values as errors -fn mkerr_libc (ret: Int) -> IoResult<()> { - if !ret.is_zero() { - Err(last_error()) - } else { - Ok(()) - } -} - -// windows has zero values as errors -#[cfg(windows)] -fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> { - if ret == 0 { - Err(last_error()) - } else { - Ok(()) - } -} - #[cfg(windows)] #[inline] fn retry (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020 @@ -199,62 +172,6 @@ impl rtio::IoFactory for IoFactory { addrinfo::GetAddrInfoRequest::run(host, servname, hint) } - // filesystem operations - fn fs_from_raw_fd(&mut self, fd: c_int, close: rtio::CloseBehavior) - -> Box { - let close = match close { - rtio::CloseSynchronously | rtio::CloseAsynchronously => true, - rtio::DontClose => false - }; - box file::FileDesc::new(fd, close) as Box - } - fn fs_open(&mut self, path: &CString, fm: rtio::FileMode, - fa: rtio::FileAccess) - -> IoResult> - { - file::open(path, fm, fa).map(|fd| box fd as Box) - } - fn fs_unlink(&mut self, path: &CString) -> IoResult<()> { - file::unlink(path) - } - fn fs_stat(&mut self, path: &CString) -> IoResult { - file::stat(path) - } - fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()> { - file::mkdir(path, mode) - } - fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()> { - file::chmod(path, mode) - } - fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> { - file::rmdir(path) - } - fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> { - file::rename(path, to) - } - fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult> { - file::readdir(path) - } - fn fs_lstat(&mut self, path: &CString) -> IoResult { - file::lstat(path) - } - fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> { - file::chown(path, uid, gid) - } - fn fs_readlink(&mut self, path: &CString) -> IoResult { - file::readlink(path) - } - fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> { - file::symlink(src, dst) - } - fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> { - file::link(src, dst) - } - fn fs_utime(&mut self, src: &CString, atime: u64, - mtime: u64) -> IoResult<()> { - file::utime(src, atime, mtime) - } - // misc fn timer_init(&mut self) -> IoResult> { timer::Timer::new().map(|t| box t as Box) diff --git a/src/libnative/io/timer_unix.rs b/src/libnative/io/timer_unix.rs index 38895f2a8f96..c26e2e76cee6 100644 --- a/src/libnative/io/timer_unix.rs +++ b/src/libnative/io/timer_unix.rs @@ -56,7 +56,7 @@ use std::sync::atomic; use std::comm; use io::c; -use io::file::FileDesc; +use platform_imp::fs::FileDesc; use io::helper_thread::Helper; helper_init!(static HELPER: Helper) diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs index e9bee19b4982..1f3ef60e6fb2 100644 --- a/src/librustrt/rtio.rs +++ b/src/librustrt/rtio.rs @@ -50,20 +50,6 @@ pub trait RemoteCallback { fn fire(&mut self); } -/// Description of what to do when a file handle is closed -pub enum CloseBehavior { - /// Do not close this handle when the object is destroyed - DontClose, - /// Synchronously close the handle, meaning that the task will block when - /// the handle is destroyed until it has been fully closed. - CloseSynchronously, - /// Asynchronously closes a handle, meaning that the task will *not* block - /// when the handle is destroyed, but the handle will still get deallocated - /// and cleaned up (but this will happen asynchronously on the local event - /// loop). - CloseAsynchronously, -} - /// Data needed to spawn a process. Serializes the `std::io::process::Command` /// builder. pub struct ProcessConfig<'a> { @@ -202,28 +188,6 @@ pub trait IoFactory { hint: Option) -> IoResult>; - // filesystem operations - fn fs_from_raw_fd(&mut self, fd: c_int, close: CloseBehavior) - -> Box; - fn fs_open(&mut self, path: &CString, fm: FileMode, fa: FileAccess) - -> IoResult>; - fn fs_unlink(&mut self, path: &CString) -> IoResult<()>; - fn fs_stat(&mut self, path: &CString) -> IoResult; - fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()>; - fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()>; - fn fs_rmdir(&mut self, path: &CString) -> IoResult<()>; - fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()>; - fn fs_readdir(&mut self, path: &CString, flags: c_int) -> - IoResult>; - fn fs_lstat(&mut self, path: &CString) -> IoResult; - fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> - IoResult<()>; - fn fs_readlink(&mut self, path: &CString) -> IoResult; - fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()>; - fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()>; - fn fs_utime(&mut self, src: &CString, atime: u64, mtime: u64) -> - IoResult<()>; - // misc fn timer_init(&mut self) -> IoResult>; fn spawn(&mut self, cfg: ProcessConfig) @@ -296,19 +260,6 @@ pub trait RtioTimer { fn period(&mut self, msecs: u64, cb: Box); } -pub trait RtioFileStream { - fn read(&mut self, buf: &mut [u8]) -> IoResult; - fn write(&mut self, buf: &[u8]) -> IoResult<()>; - fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult; - fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()>; - fn seek(&mut self, pos: i64, whence: SeekStyle) -> IoResult; - fn tell(&self) -> IoResult; - fn fsync(&mut self) -> IoResult<()>; - fn datasync(&mut self) -> IoResult<()>; - fn truncate(&mut self, offset: i64) -> IoResult<()>; - fn fstat(&mut self) -> IoResult; -} - pub trait RtioProcess { fn id(&self) -> libc::pid_t; fn kill(&mut self, signal: int) -> IoResult<()>; @@ -399,43 +350,6 @@ pub enum ProcessExit { ExitSignal(int), } -pub enum FileMode { - Open, - Append, - Truncate, -} - -pub enum FileAccess { - Read, - Write, - ReadWrite, -} - -pub struct FileStat { - pub size: u64, - pub kind: u64, - pub perm: u64, - pub created: u64, - pub modified: u64, - pub accessed: u64, - pub device: u64, - pub inode: u64, - pub rdev: u64, - pub nlink: u64, - pub uid: u64, - pub gid: u64, - pub blksize: u64, - pub blocks: u64, - pub flags: u64, - pub gen: u64, -} - -pub enum SeekStyle { - SeekSet, - SeekEnd, - SeekCur, -} - pub struct AddrinfoHint { pub family: uint, pub socktype: uint, diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index e76046bac059..5c2a5c3512d3 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -52,28 +52,25 @@ fs::unlink(&path); */ -use c_str::ToCStr; use clone::Clone; use io::standard_error; -use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; +use io::{FilePermission, Write, Open, FileAccess, FileMode}; use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader}; -use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; +use io::{Read, Truncate, ReadWrite, Append}; use io::UpdateIoError; use io; use iter::{Iterator, Extend}; -use kinds::Send; -use libc; use option::{Some, None, Option}; -use boxed::Box; use path::{Path, GenericPath}; use path; use result::{Err, Ok}; -use rt::rtio::LocalIo; -use rt::rtio; use slice::SlicePrelude; use string::String; use vec::Vec; +use sys::fs as fs_imp; +use sys_common; + /// Unconstrained file access type that exposes read and write operations /// /// Can be constructed via `File::open()`, `File::create()`, and @@ -86,11 +83,17 @@ use vec::Vec; /// configured at creation time, via the `FileAccess` parameter to /// `File::open_mode()`. pub struct File { - fd: Box, + fd: fs_imp::FileDesc, path: Path, last_nread: int, } +impl sys_common::AsFileDesc for File { + fn as_fd(&self) -> &fs_imp::FileDesc { + &self.fd + } +} + impl File { /// Open a file at `path` in the mode specified by the `mode` and `access` /// arguments @@ -133,26 +136,13 @@ impl File { pub fn open_mode(path: &Path, mode: FileMode, access: FileAccess) -> IoResult { - let rtio_mode = match mode { - Open => rtio::Open, - Append => rtio::Append, - Truncate => rtio::Truncate, - }; - let rtio_access = match access { - Read => rtio::Read, - Write => rtio::Write, - ReadWrite => rtio::ReadWrite, - }; - let err = LocalIo::maybe_raise(|io| { - io.fs_open(&path.to_c_str(), rtio_mode, rtio_access).map(|fd| { - File { - path: path.clone(), - fd: fd, - last_nread: -1 - } - }) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't open file", |e| { + fs_imp::open(path, mode, access).map(|fd| { + File { + path: path.clone(), + fd: fd, + last_nread: -1 + } + }).update_err("couldn't open file", |e| { format!("{}; path={}; mode={}; access={}", e, path.display(), mode_string(mode), access_string(access)) }) @@ -194,7 +184,7 @@ impl File { /// ``` pub fn create(path: &Path) -> IoResult { File::open_mode(path, Truncate, Write) - .update_desc("couldn't create file") + .update_desc("couldn't create file") } /// Returns the original path which was used to open this file. @@ -206,9 +196,9 @@ impl File { /// device. This will flush any internal buffers necessary to perform this /// operation. pub fn fsync(&mut self) -> IoResult<()> { - let err = self.fd.fsync().map_err(IoError::from_rtio_error); - err.update_err("couldn't fsync file", - |e| format!("{}; path={}", e, self.path.display())) + self.fd.fsync() + .update_err("couldn't fsync file", + |e| format!("{}; path={}", e, self.path.display())) } /// This function is similar to `fsync`, except that it may not synchronize @@ -216,9 +206,9 @@ impl File { /// must synchronize content, but don't need the metadata on disk. The goal /// of this method is to reduce disk operations. pub fn datasync(&mut self) -> IoResult<()> { - let err = self.fd.datasync().map_err(IoError::from_rtio_error); - err.update_err("couldn't datasync file", - |e| format!("{}; path={}", e, self.path.display())) + self.fd.datasync() + .update_err("couldn't datasync file", + |e| format!("{}; path={}", e, self.path.display())) } /// Either truncates or extends the underlying file, updating the size of @@ -230,10 +220,9 @@ impl File { /// will be extended to `size` and have all of the intermediate data filled /// in with 0s. pub fn truncate(&mut self, size: i64) -> IoResult<()> { - let err = self.fd.truncate(size).map_err(IoError::from_rtio_error); - err.update_err("couldn't truncate file", |e| { - format!("{}; path={}; size={}", e, self.path.display(), size) - }) + self.fd.truncate(size) + .update_err("couldn't truncate file", |e| + format!("{}; path={}; size={}", e, self.path.display(), size)) } /// Returns true if the stream has reached the end of the file. @@ -251,12 +240,9 @@ impl File { /// Queries information about the underlying file. pub fn stat(&mut self) -> IoResult { - let err = match self.fd.fstat() { - Ok(s) => Ok(from_rtio(s)), - Err(e) => Err(IoError::from_rtio_error(e)), - }; - err.update_err("couldn't fstat file", - |e| format!("{}; path={}", e, self.path.display())) + self.fd.fstat() + .update_err("couldn't fstat file", |e| + format!("{}; path={}", e, self.path.display())) } } @@ -282,41 +268,9 @@ impl File { /// user lacks permissions to remove the file, or if some other filesystem-level /// error occurs. pub fn unlink(path: &Path) -> IoResult<()> { - return match do_unlink(path) { - Ok(()) => Ok(()), - Err(e) => { - // On unix, a readonly file can be successfully removed. On windows, - // however, it cannot. To keep the two platforms in line with - // respect to their behavior, catch this case on windows, attempt to - // change it to read-write, and then remove the file. - if cfg!(windows) && e.kind == io::PermissionDenied { - let stat = match stat(path) { - Ok(stat) => stat, - Err(..) => return Err(e), - }; - if stat.perm.intersects(io::USER_WRITE) { return Err(e) } - - match chmod(path, stat.perm | io::USER_WRITE) { - Ok(()) => do_unlink(path), - Err(..) => { - // Try to put it back as we found it - let _ = chmod(path, stat.perm); - Err(e) - } - } - } else { - Err(e) - } - } - }; - - fn do_unlink(path: &Path) -> IoResult<()> { - let err = LocalIo::maybe_raise(|io| { - io.fs_unlink(&path.to_c_str()) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't unlink path", - |e| format!("{}; path={}", e, path.display())) - } + fs_imp::unlink(path) + .update_err("couldn't unlink path", |e| + format!("{}; path={}", e, path.display())) } /// Given a path, query the file system to get information about a file, @@ -341,12 +295,9 @@ pub fn unlink(path: &Path) -> IoResult<()> { /// to perform a `stat` call on the given `path` or if there is no entry in the /// filesystem at the provided path. pub fn stat(path: &Path) -> IoResult { - let err = match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) { - Ok(s) => Ok(from_rtio(s)), - Err(e) => Err(IoError::from_rtio_error(e)), - }; - err.update_err("couldn't stat path", - |e| format!("{}; path={}", e, path.display())) + fs_imp::stat(path) + .update_err("couldn't stat path", |e| + format!("{}; path={}", e, path.display())) } /// Perform the same operation as the `stat` function, except that this @@ -358,53 +309,9 @@ pub fn stat(path: &Path) -> IoResult { /// /// See `stat` pub fn lstat(path: &Path) -> IoResult { - let err = match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) { - Ok(s) => Ok(from_rtio(s)), - Err(e) => Err(IoError::from_rtio_error(e)), - }; - err.update_err("couldn't lstat path", - |e| format!("{}; path={}", e, path.display())) -} - -fn from_rtio(s: rtio::FileStat) -> FileStat { - #[cfg(windows)] - type Mode = libc::c_int; - #[cfg(unix)] - type Mode = libc::mode_t; - - let rtio::FileStat { - size, kind, perm, created, modified, - accessed, device, inode, rdev, - nlink, uid, gid, blksize, blocks, flags, gen - } = s; - - FileStat { - size: size, - kind: match (kind as Mode) & libc::S_IFMT { - libc::S_IFREG => io::TypeFile, - libc::S_IFDIR => io::TypeDirectory, - libc::S_IFIFO => io::TypeNamedPipe, - libc::S_IFBLK => io::TypeBlockSpecial, - libc::S_IFLNK => io::TypeSymlink, - _ => io::TypeUnknown, - }, - perm: FilePermission::from_bits_truncate(perm as u32), - created: created, - modified: modified, - accessed: accessed, - unstable: UnstableFileStat { - device: device, - inode: inode, - rdev: rdev, - nlink: nlink, - uid: uid, - gid: gid, - blksize: blksize, - blocks: blocks, - flags: flags, - gen: gen, - }, - } + fs_imp::lstat(path) + .update_err("couldn't lstat path", |e| + format!("{}; path={}", e, path.display())) } /// Rename a file or directory to a new name. @@ -424,12 +331,9 @@ fn from_rtio(s: rtio::FileStat) -> FileStat { /// the process lacks permissions to view the contents, or if some other /// intermittent I/O error occurs. pub fn rename(from: &Path, to: &Path) -> IoResult<()> { - let err = LocalIo::maybe_raise(|io| { - io.fs_rename(&from.to_c_str(), &to.to_c_str()) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't rename path", |e| { - format!("{}; from={}; to={}", e, from.display(), to.display()) - }) + fs_imp::rename(from, to) + .update_err("couldn't rename path", |e| + format!("{}; from={}; to={}", e, from.display(), to.display())) } /// Copies the contents of one file to another. This function will also @@ -462,8 +366,9 @@ pub fn rename(from: &Path, to: &Path) -> IoResult<()> { /// being created and then destroyed by this operation. pub fn copy(from: &Path, to: &Path) -> IoResult<()> { fn update_err(result: IoResult, from: &Path, to: &Path) -> IoResult { - result.update_err("couldn't copy path", - |e| format!("{}; from={}; to={}", e, from.display(), to.display())) + result.update_err("couldn't copy path", |e| { + format!("{}; from={}; to={}", e, from.display(), to.display()) + }) } if !from.is_file() { @@ -512,45 +417,33 @@ pub fn copy(from: &Path, to: &Path) -> IoResult<()> { /// the process lacks permissions to change the attributes of the file, or if /// some other I/O error is encountered. pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> { - let err = LocalIo::maybe_raise(|io| { - io.fs_chmod(&path.to_c_str(), mode.bits() as uint) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't chmod path", |e| { - format!("{}; path={}; mode={}", e, path.display(), mode) - }) + fs_imp::chmod(path, mode.bits() as uint) + .update_err("couldn't chmod path", |e| + format!("{}; path={}; mode={}", e, path.display(), mode)) } /// Change the user and group owners of a file at the specified path. pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> { - let err = LocalIo::maybe_raise(|io| { - io.fs_chown(&path.to_c_str(), uid, gid) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't chown path", |e| { - format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid) - }) + fs_imp::chown(path, uid, gid) + .update_err("couldn't chown path", |e| + format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)) } /// Creates a new hard link on the filesystem. The `dst` path will be a /// link pointing to the `src` path. Note that systems often require these /// two paths to both be located on the same filesystem. pub fn link(src: &Path, dst: &Path) -> IoResult<()> { - let err = LocalIo::maybe_raise(|io| { - io.fs_link(&src.to_c_str(), &dst.to_c_str()) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't link path", |e| { - format!("{}; src={}; dest={}", e, src.display(), dst.display()) - }) + fs_imp::link(src, dst) + .update_err("couldn't link path", |e| + format!("{}; src={}; dest={}", e, src.display(), dst.display())) } /// Creates a new symbolic link on the filesystem. The `dst` path will be a /// symlink pointing to the `src` path. pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { - let err = LocalIo::maybe_raise(|io| { - io.fs_symlink(&src.to_c_str(), &dst.to_c_str()) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't symlink path", |e| { - format!("{}; src={}; dest={}", e, src.display(), dst.display()) - }) + fs_imp::symlink(src, dst) + .update_err("couldn't symlink path", |e| + format!("{}; src={}; dest={}", e, src.display(), dst.display())) } /// Reads a symlink, returning the file that the symlink points to. @@ -560,11 +453,9 @@ pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { /// This function will return an error on failure. Failure conditions include /// reading a file that does not exist or reading a file which is not a symlink. pub fn readlink(path: &Path) -> IoResult { - let err = LocalIo::maybe_raise(|io| { - Ok(Path::new(try!(io.fs_readlink(&path.to_c_str())))) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't resolve symlink for path", - |e| format!("{}; path={}", e, path.display())) + fs_imp::readlink(path) + .update_err("couldn't resolve symlink for path", |e| + format!("{}; path={}", e, path.display())) } /// Create a new, empty directory at the provided path @@ -585,12 +476,9 @@ pub fn readlink(path: &Path) -> IoResult { /// This function will return an error if the user lacks permissions to make a /// new directory at the provided `path`, or if the directory already exists. pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> { - let err = LocalIo::maybe_raise(|io| { - io.fs_mkdir(&path.to_c_str(), mode.bits() as uint) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't create directory", |e| { - format!("{}; path={}; mode={}", e, path.display(), mode) - }) + fs_imp::mkdir(path, mode.bits() as uint) + .update_err("couldn't create directory", |e| + format!("{}; path={}; mode={}", e, path.display(), mode)) } /// Remove an existing, empty directory @@ -610,11 +498,9 @@ pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> { /// This function will return an error if the user lacks permissions to remove /// the directory at the provided `path`, or if the directory isn't empty. pub fn rmdir(path: &Path) -> IoResult<()> { - let err = LocalIo::maybe_raise(|io| { - io.fs_rmdir(&path.to_c_str()) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't remove directory", - |e| format!("{}; path={}", e, path.display())) + fs_imp::rmdir(path) + .update_err("couldn't remove directory", |e| + format!("{}; path={}", e, path.display())) } /// Retrieve a vector containing all entries within a provided directory @@ -650,13 +536,9 @@ pub fn rmdir(path: &Path) -> IoResult<()> { /// the process lacks permissions to view the contents or if the `path` points /// at a non-directory file pub fn readdir(path: &Path) -> IoResult> { - let err = LocalIo::maybe_raise(|io| { - Ok(try!(io.fs_readdir(&path.to_c_str(), 0)).into_iter().map(|a| { - Path::new(a) - }).collect()) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't read directory", - |e| format!("{}; path={}", e, path.display())) + fs_imp::readdir(path) + .update_err("couldn't read directory", + |e| format!("{}; path={}", e, path.display())) } /// Returns an iterator which will recursively walk the directory structure @@ -666,8 +548,7 @@ pub fn readdir(path: &Path) -> IoResult> { pub fn walk_dir(path: &Path) -> IoResult { Ok(Directories { stack: try!(readdir(path).update_err("couldn't walk directory", - |e| format!("{}; path={}", - e, path.display()))) + |e| format!("{}; path={}", e, path.display()))) }) } @@ -681,12 +562,7 @@ impl Iterator for Directories { match self.stack.pop() { Some(path) => { if path.is_dir() { - let result = readdir(&path) - .update_err("couldn't advance Directories iterator", - |e| format!("{}; path={}", - e, path.display())); - - match result { + match readdir(&path) { Ok(dirs) => { self.stack.extend(dirs.into_iter()); } Err(..) => {} } @@ -804,11 +680,9 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> { /// be in milliseconds. // FIXME(#10301) these arguments should not be u64 pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> { - let err = LocalIo::maybe_raise(|io| { - io.fs_utime(&path.to_c_str(), atime, mtime) - }).map_err(IoError::from_rtio_error); - err.update_err("couldn't change_file_times", - |e| format!("{}; path={}", e, path.display())) + fs_imp::utime(path, atime, mtime) + .update_err("couldn't change_file_times", |e| + format!("{}; path={}", e, path.display())) } impl Reader for File { @@ -819,12 +693,11 @@ impl Reader for File { e, file.path.display())) } - let result = update_err(self.fd.read(buf) - .map_err(IoError::from_rtio_error), self); + let result = update_err(self.fd.read(buf), self); match result { Ok(read) => { - self.last_nread = read; + self.last_nread = read as int; match read { 0 => update_err(Err(standard_error(io::EndOfFile)), self), _ => Ok(read as uint) @@ -837,32 +710,27 @@ impl Reader for File { impl Writer for File { fn write(&mut self, buf: &[u8]) -> IoResult<()> { - let err = self.fd.write(buf).map_err(IoError::from_rtio_error); - err.update_err("couldn't write to file", - |e| format!("{}; path={}", e, self.path.display())) + self.fd.write(buf) + .update_err("couldn't write to file", + |e| format!("{}; path={}", e, self.path.display())) } } impl Seek for File { fn tell(&self) -> IoResult { - let err = self.fd.tell().map_err(IoError::from_rtio_error); - err.update_err("couldn't retrieve file cursor (`tell`)", - |e| format!("{}; path={}", e, self.path.display())) + self.fd.tell() + .update_err("couldn't retrieve file cursor (`tell`)", + |e| format!("{}; path={}", e, self.path.display())) } fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { - let style = match style { - SeekSet => rtio::SeekSet, - SeekCur => rtio::SeekCur, - SeekEnd => rtio::SeekEnd, - }; let err = match self.fd.seek(pos, style) { Ok(_) => { // successful seek resets EOF indicator self.last_nread = -1; Ok(()) } - Err(e) => Err(IoError::from_rtio_error(e)), + Err(e) => Err(e), }; err.update_err("couldn't seek in file", |e| format!("{}; path={}", e, self.path.display())) @@ -942,6 +810,8 @@ fn access_string(access: FileAccess) -> &'static str { #[cfg(test)] #[allow(unused_imports)] +#[allow(unused_variables)] +#[allow(unused_mut)] mod test { use prelude::*; use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite}; diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 78abbb9f80df..03c073c1477d 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -316,6 +316,7 @@ impl IoError { err.detail = Some(os::error_string(errno).as_slice().chars() .map(|c| c.to_lowercase()).collect()) } + err } /// Retrieve the last error to occur as a (detailed) IoError. diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 7bae67c0aa6b..98644cfc7e99 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -36,11 +36,11 @@ use kinds::Send; use libc; use option::{Option, Some, None}; use boxed::Box; +use sys::fs::FileDesc; use result::{Ok, Err}; use rt; use rt::local::Local; use rt::task::Task; -use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY}; use slice::SlicePrelude; use str::StrPrelude; use uint; @@ -75,14 +75,14 @@ use uint; // case pipe also doesn't work, but magically file does! enum StdSource { TTY(Box), - File(Box), + File(FileDesc), } fn src(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T { LocalIo::maybe_raise(|io| { Ok(match io.tty_open(fd, readable) { Ok(tty) => f(TTY(tty)), - Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))), + Err(_) => f(File(FileDesc::new(fd, false))), }) }).map_err(IoError::from_rtio_error).unwrap() } @@ -278,10 +278,10 @@ impl Reader for StdReader { // print!'d prompt not being shown until after the user hits // enter. flush(); - tty.read(buf) + tty.read(buf).map_err(IoError::from_rtio_error) }, File(ref mut file) => file.read(buf).map(|i| i as uint), - }.map_err(IoError::from_rtio_error); + }; match ret { // When reading a piped stdin, libuv will return 0-length reads when // stdin reaches EOF. For pretty much all other streams it will @@ -372,9 +372,9 @@ impl Writer for StdWriter { let max_size = if cfg!(windows) {8192} else {uint::MAX}; for chunk in buf.chunks(max_size) { try!(match self.inner { - TTY(ref mut tty) => tty.write(chunk), + TTY(ref mut tty) => tty.write(chunk).map_err(IoError::from_rtio_error), File(ref mut file) => file.write(chunk), - }.map_err(IoError::from_rtio_error)) + }) } Ok(()) } diff --git a/src/libstd/platform_imp/unix/fs.rs b/src/libstd/platform_imp/unix/fs.rs new file mode 100644 index 000000000000..3dcd99859e8c --- /dev/null +++ b/src/libstd/platform_imp/unix/fs.rs @@ -0,0 +1,411 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Blocking posix-based file I/O + +use libc::{mod, c_int, c_void}; +use c_str::CString; +use mem; +use io; + +use prelude::*; + +use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; +use io::{IoResult, FileStat, SeekStyle, Reader}; +use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; +use result::{Ok, Err}; +use sys::retry; +use sys_common::{keep_going, eof, mkerr_libc}; + +pub use path::PosixPath as Path; + +pub type fd_t = libc::c_int; + +pub struct FileDesc { + /// The underlying C file descriptor. + fd: fd_t, + + /// Whether to close the file descriptor on drop. + close_on_drop: bool, +} + +impl FileDesc { + pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc { + FileDesc { fd: fd, close_on_drop: close_on_drop } + } + + pub fn read(&self, buf: &mut [u8]) -> IoResult { + let ret = retry(|| unsafe { + libc::read(self.fd(), + buf.as_mut_ptr() as *mut libc::c_void, + buf.len() as libc::size_t) + }); + if ret == 0 { + Err(eof()) + } else if ret < 0 { + Err(super::last_error()) + } else { + Ok(ret as uint) + } + } + pub fn write(&self, buf: &[u8]) -> IoResult<()> { + let ret = keep_going(buf, |buf, len| { + unsafe { + libc::write(self.fd(), buf as *const libc::c_void, + len as libc::size_t) as i64 + } + }); + if ret < 0 { + Err(super::last_error()) + } else { + Ok(()) + } + } + + pub fn fd(&self) -> fd_t { self.fd } + + pub fn seek(&self, pos: i64, whence: SeekStyle) -> IoResult { + let whence = match whence { + SeekSet => libc::SEEK_SET, + SeekEnd => libc::SEEK_END, + SeekCur => libc::SEEK_CUR, + }; + let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) }; + if n < 0 { + Err(super::last_error()) + } else { + Ok(n as u64) + } + } + + pub fn tell(&self) -> IoResult { + let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) }; + if n < 0 { + Err(super::last_error()) + } else { + Ok(n as u64) + } + } + + pub fn fsync(&self) -> IoResult<()> { + mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) })) + } + + pub fn datasync(&self) -> IoResult<()> { + return mkerr_libc(os_datasync(self.fd())); + + #[cfg(any(target_os = "macos", target_os = "ios"))] + fn os_datasync(fd: c_int) -> c_int { + unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) } + } + #[cfg(target_os = "linux")] + fn os_datasync(fd: c_int) -> c_int { + retry(|| unsafe { libc::fdatasync(fd) }) + } + #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))] + fn os_datasync(fd: c_int) -> c_int { + retry(|| unsafe { libc::fsync(fd) }) + } + } + + pub fn truncate(&self, offset: i64) -> IoResult<()> { + mkerr_libc(retry(|| unsafe { + libc::ftruncate(self.fd(), offset as libc::off_t) + })) + } + + pub fn fstat(&self) -> IoResult { + let mut stat: libc::stat = unsafe { mem::zeroed() }; + match unsafe { libc::fstat(self.fd(), &mut stat) } { + 0 => Ok(mkstat(&stat)), + _ => Err(super::last_error()), + } + } + + /// Extract the actual filedescriptor without closing it. + pub fn unwrap(self) -> fd_t { + let fd = self.fd; + unsafe { mem::forget(self) }; + fd + } +} + +/* + +impl RtioTTY for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + self.inner_read(buf) + } + fn write(&mut self, buf: &[u8]) -> IoResult<()> { + self.inner_write(buf) + } + fn set_raw(&mut self, _raw: bool) -> IoResult<()> { + Err(super::unimpl()) + } + fn get_winsize(&mut self) -> IoResult<(int, int)> { + Err(super::unimpl()) + } + fn isatty(&self) -> bool { false } +} +*/ + +impl Drop for FileDesc { + fn drop(&mut self) { + // closing stdio file handles makes no sense, so never do it. Also, note + // that errors are ignored when closing a file descriptor. The reason + // for this is that if an error occurs we don't actually know if the + // file descriptor was closed or not, and if we retried (for something + // like EINTR), we might close another valid file descriptor (opened + // after we closed ours. + if self.close_on_drop && self.fd > libc::STDERR_FILENO { + let n = unsafe { libc::close(self.fd) }; + if n != 0 { + println!("error {} when closing file descriptor {}", n, self.fd); + } + } + } +} + +pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult { + let flags = match fm { + Open => 0, + Append => libc::O_APPEND, + Truncate => libc::O_TRUNC, + }; + // Opening with a write permission must silently create the file. + let (flags, mode) = match fa { + Read => (flags | libc::O_RDONLY, 0), + Write => (flags | libc::O_WRONLY | libc::O_CREAT, + libc::S_IRUSR | libc::S_IWUSR), + ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT, + libc::S_IRUSR | libc::S_IWUSR), + }; + + let path = path.to_c_str(); + match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) { + -1 => Err(super::last_error()), + fd => Ok(FileDesc::new(fd, true)), + } +} + +pub fn mkdir(p: &Path, mode: uint) -> IoResult<()> { + let p = p.to_c_str(); + mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) }) +} + +pub fn readdir(p: &Path) -> IoResult> { + use libc::{dirent_t}; + use libc::{opendir, readdir_r, closedir}; + + fn prune(root: &CString, dirs: Vec) -> Vec { + let root = unsafe { CString::new(root.as_ptr(), false) }; + let root = Path::new(root); + + dirs.into_iter().filter(|path| { + path.as_vec() != b"." && path.as_vec() != b".." + }).map(|path| root.join(path)).collect() + } + + extern { + fn rust_dirent_t_size() -> libc::c_int; + fn rust_list_dir_val(ptr: *mut dirent_t) -> *const libc::c_char; + } + + let size = unsafe { rust_dirent_t_size() }; + let mut buf = Vec::::with_capacity(size as uint); + let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t; + + let p = p.to_c_str(); + let dir_ptr = unsafe {opendir(p.as_ptr())}; + + if dir_ptr as uint != 0 { + let mut paths = vec!(); + let mut entry_ptr = 0 as *mut dirent_t; + while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } { + if entry_ptr.is_null() { break } + let cstr = unsafe { + CString::new(rust_list_dir_val(entry_ptr), false) + }; + paths.push(Path::new(cstr)); + } + assert_eq!(unsafe { closedir(dir_ptr) }, 0); + Ok(prune(&p, paths)) + } else { + Err(super::last_error()) + } +} + +pub fn unlink(p: &Path) -> IoResult<()> { + let p = p.to_c_str(); + mkerr_libc(unsafe { libc::unlink(p.as_ptr()) }) +} + +pub fn rename(old: &Path, new: &Path) -> IoResult<()> { + let old = old.to_c_str(); + let new = new.to_c_str(); + mkerr_libc(unsafe { + libc::rename(old.as_ptr(), new.as_ptr()) + }) +} + +pub fn chmod(p: &Path, mode: uint) -> IoResult<()> { + let p = p.to_c_str(); + mkerr_libc(retry(|| unsafe { + libc::chmod(p.as_ptr(), mode as libc::mode_t) + })) +} + +pub fn rmdir(p: &Path) -> IoResult<()> { + let p = p.to_c_str(); + mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) }) +} + +pub fn chown(p: &Path, uid: int, gid: int) -> IoResult<()> { + let p = p.to_c_str(); + mkerr_libc(retry(|| unsafe { + libc::chown(p.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) + })) +} + +pub fn readlink(p: &Path) -> IoResult { + let c_path = p.to_c_str(); + let p = c_path.as_ptr(); + let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) }; + if len == -1 { + len = 1024; // FIXME: read PATH_MAX from C ffi? + } + let mut buf: Vec = Vec::with_capacity(len as uint); + match unsafe { + libc::readlink(p, buf.as_ptr() as *mut libc::c_char, + len as libc::size_t) as libc::c_int + } { + -1 => Err(super::last_error()), + n => { + assert!(n > 0); + unsafe { buf.set_len(n as uint); } + Ok(Path::new(buf)) + } + } +} + +pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { + let src = src.to_c_str(); + let dst = dst.to_c_str(); + mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) }) +} + +pub fn link(src: &Path, dst: &Path) -> IoResult<()> { + let src = src.to_c_str(); + let dst = dst.to_c_str(); + mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) }) +} + +fn mkstat(stat: &libc::stat) -> FileStat { + // FileStat times are in milliseconds + fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } + + #[cfg(not(any(target_os = "linux", target_os = "android")))] + fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 } + #[cfg(any(target_os = "linux", target_os = "android"))] + fn flags(_stat: &libc::stat) -> u64 { 0 } + + #[cfg(not(any(target_os = "linux", target_os = "android")))] + fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 } + #[cfg(any(target_os = "linux", target_os = "android"))] + fn gen(_stat: &libc::stat) -> u64 { 0 } + + FileStat { + size: stat.st_size as u64, + kind: match (stat.st_mode as libc::mode_t) & libc::S_IFMT { + libc::S_IFREG => io::TypeFile, + libc::S_IFDIR => io::TypeDirectory, + libc::S_IFIFO => io::TypeNamedPipe, + libc::S_IFBLK => io::TypeBlockSpecial, + libc::S_IFLNK => io::TypeSymlink, + _ => io::TypeUnknown, + }, + perm: FilePermission::from_bits_truncate(stat.st_mode as u32), + created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64), + modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64), + accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64), + unstable: UnstableFileStat { + device: stat.st_dev as u64, + inode: stat.st_ino as u64, + rdev: stat.st_rdev as u64, + nlink: stat.st_nlink as u64, + uid: stat.st_uid as u64, + gid: stat.st_gid as u64, + blksize: stat.st_blksize as u64, + blocks: stat.st_blocks as u64, + flags: flags(stat), + gen: gen(stat), + }, + } +} + +pub fn stat(p: &Path) -> IoResult { + let p = p.to_c_str(); + let mut stat: libc::stat = unsafe { mem::zeroed() }; + match unsafe { libc::stat(p.as_ptr(), &mut stat) } { + 0 => Ok(mkstat(&stat)), + _ => Err(super::last_error()), + } +} + +pub fn lstat(p: &Path) -> IoResult { + let p = p.to_c_str(); + let mut stat: libc::stat = unsafe { mem::zeroed() }; + match unsafe { libc::lstat(p.as_ptr(), &mut stat) } { + 0 => Ok(mkstat(&stat)), + _ => Err(super::last_error()), + } +} + +pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> { + let p = p.to_c_str(); + let buf = libc::utimbuf { + actime: (atime / 1000) as libc::time_t, + modtime: (mtime / 1000) as libc::time_t, + }; + mkerr_libc(unsafe { libc::utime(p.as_ptr(), &buf) }) +} + +#[cfg(test)] +mod tests { + use super::FileDesc; + use libc; + use os; + use prelude::*; + + #[cfg_attr(target_os = "freebsd", ignore)] // hmm, maybe pipes have a tiny buffer + #[test] + fn test_file_desc() { + // Run this test with some pipes so we don't have to mess around with + // opening or closing files. + let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() }; + let mut reader = FileDesc::new(reader, true); + let mut writer = FileDesc::new(writer, true); + + writer.write(b"test").ok().unwrap(); + let mut buf = [0u8, ..4]; + match reader.read(buf) { + Ok(4) => { + assert_eq!(buf[0], 't' as u8); + assert_eq!(buf[1], 'e' as u8); + assert_eq!(buf[2], 's' as u8); + assert_eq!(buf[3], 't' as u8); + } + r => panic!("invalid read: {}", r), + } + + assert!(writer.read(buf).is_err()); + assert!(reader.write(buf).is_err()); + } +} diff --git a/src/libnative/io/file_windows.rs b/src/libstd/platform_imp/windows/fs.rs similarity index 52% rename from src/libnative/io/file_windows.rs rename to src/libstd/platform_imp/windows/fs.rs index eb4d4f22132d..a07688b2fed0 100644 --- a/src/libnative/io/file_windows.rs +++ b/src/libstd/platform_imp/windows/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -12,42 +12,40 @@ use alloc::arc::Arc; use libc::{mod, c_int}; -use std::c_str::CString; -use std::mem; -use std::os::windows::fill_utf16_buf_and_decode; -use std::ptr; -use std::rt::rtio; -use std::rt::rtio::{IoResult, IoError}; -use std::str; +use c_str::CString; +use mem; +use os::windows::fill_utf16_buf_and_decode; +use path; +use ptr; +use str; +use io; + +use prelude::*; +use sys; +use sys_common::{keep_going, eof, mkerr_libc}; + +use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; +use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader}; +use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; + +pub use path::WindowsPath as Path; pub type fd_t = libc::c_int; -struct Inner { - fd: fd_t, +pub struct FileDesc { + /// The underlying C file descriptor. + pub fd: fd_t, + + /// Whether to close the file descriptor on drop. close_on_drop: bool, } -pub struct FileDesc { - inner: Arc -} - impl FileDesc { - /// Create a `FileDesc` from an open C file descriptor. - /// - /// The `FileDesc` will take ownership of the specified file descriptor and - /// close it upon destruction if the `close_on_drop` flag is true, otherwise - /// it will not close the file descriptor when this `FileDesc` is dropped. - /// - /// Note that all I/O operations done on this object will be *blocking*, but - /// they do not require the runtime to be active. pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc { - FileDesc { inner: Arc::new(Inner { - fd: fd, - close_on_drop: close_on_drop - }) } + FileDesc { fd: fd, close_on_drop: close_on_drop } } - pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult { + pub fn read(&self, buf: &mut [u8]) -> IoResult { let mut read = 0; let ret = unsafe { libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID, @@ -60,7 +58,8 @@ impl FileDesc { Err(super::last_error()) } } - pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> { + + pub fn write(&self, buf: &[u8]) -> IoResult<()> { let mut cur = buf.as_ptr(); let mut remaining = buf.len(); while remaining > 0 { @@ -80,7 +79,7 @@ impl FileDesc { Ok(()) } - pub fn fd(&self) -> fd_t { self.inner.fd } + pub fn fd(&self) -> fd_t { self.fd } pub fn handle(&self) -> libc::HANDLE { unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE } @@ -88,153 +87,67 @@ impl FileDesc { // A version of seek that takes &self so that tell can call it // - the private seek should of course take &mut self. - fn seek_common(&self, pos: i64, style: rtio::SeekStyle) -> IoResult { + fn seek_common(&self, pos: i64, style: SeekStyle) -> IoResult { let whence = match style { - rtio::SeekSet => libc::FILE_BEGIN, - rtio::SeekEnd => libc::FILE_END, - rtio::SeekCur => libc::FILE_CURRENT, + SeekSet => libc::FILE_BEGIN, + SeekEnd => libc::FILE_END, + SeekCur => libc::FILE_CURRENT, }; unsafe { let mut newpos = 0; - match libc::SetFilePointerEx(self.handle(), pos, &mut newpos, - whence) { + match libc::SetFilePointerEx(self.handle(), pos, &mut newpos, whence) { 0 => Err(super::last_error()), _ => Ok(newpos as u64), } } } -} - -impl rtio::RtioFileStream for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner_read(buf).map(|i| i as int) - } - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner_write(buf) - } - - fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult { - let mut read = 0; - let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() }; - overlap.Offset = offset as libc::DWORD; - overlap.OffsetHigh = (offset >> 32) as libc::DWORD; - let ret = unsafe { - libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID, - buf.len() as libc::DWORD, &mut read, - &mut overlap) - }; - if ret != 0 { - Ok(read as int) - } else { - Err(super::last_error()) - } - } - fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> IoResult<()> { - let mut cur = buf.as_ptr(); - let mut remaining = buf.len(); - let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() }; - while remaining > 0 { - overlap.Offset = offset as libc::DWORD; - overlap.OffsetHigh = (offset >> 32) as libc::DWORD; - let mut amt = 0; - let ret = unsafe { - libc::WriteFile(self.handle(), cur as libc::LPVOID, - remaining as libc::DWORD, &mut amt, - &mut overlap) - }; - if ret != 0 { - remaining -= amt as uint; - cur = unsafe { cur.offset(amt as int) }; - offset += amt as u64; - } else { - return Err(super::last_error()) - } - } - Ok(()) - } - - fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult { + pub fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult { self.seek_common(pos, style) } - fn tell(&self) -> IoResult { - self.seek_common(0, rtio::SeekCur) + pub fn tell(&self) -> IoResult { + self.seek_common(0, SeekCur) } - fn fsync(&mut self) -> IoResult<()> { + pub fn fsync(&mut self) -> IoResult<()> { super::mkerr_winbool(unsafe { libc::FlushFileBuffers(self.handle()) }) } - fn datasync(&mut self) -> IoResult<()> { return self.fsync(); } + pub fn datasync(&mut self) -> IoResult<()> { return self.fsync(); } - fn truncate(&mut self, offset: i64) -> IoResult<()> { + pub fn truncate(&mut self, offset: i64) -> IoResult<()> { let orig_pos = try!(self.tell()); - let _ = try!(self.seek(offset, rtio::SeekSet)); + let _ = try!(self.seek(offset, SeekSet)); let ret = unsafe { match libc::SetEndOfFile(self.handle()) { 0 => Err(super::last_error()), _ => Ok(()) } }; - let _ = self.seek(orig_pos as i64, rtio::SeekSet); + let _ = self.seek(orig_pos as i64, SeekSet); return ret; } - fn fstat(&mut self) -> IoResult { + pub fn fstat(&mut self) -> IoResult { let mut stat: libc::stat = unsafe { mem::zeroed() }; match unsafe { libc::fstat(self.fd(), &mut stat) } { 0 => Ok(mkstat(&stat)), _ => Err(super::last_error()), } } + + /// Extract the actual filedescriptor without closing it. + pub fn unwrap(self) -> fd_t { + let fd = self.fd; + unsafe { mem::forget(self) }; + fd + } } -impl rtio::RtioPipe for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner_read(buf) - } - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner_write(buf) - } - fn clone(&self) -> Box { - box FileDesc { inner: self.inner.clone() } as Box - } - - // Only supported on named pipes currently. Note that this doesn't have an - // impact on the std::io primitives, this is never called via - // std::io::PipeStream. If the functionality is exposed in the future, then - // these methods will need to be implemented. - fn close_read(&mut self) -> IoResult<()> { - Err(super::unimpl()) - } - fn close_write(&mut self) -> IoResult<()> { - Err(super::unimpl()) - } - fn set_timeout(&mut self, _t: Option) {} - fn set_read_timeout(&mut self, _t: Option) {} - fn set_write_timeout(&mut self, _t: Option) {} -} - -impl rtio::RtioTTY for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.inner_read(buf) - } - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.inner_write(buf) - } - fn set_raw(&mut self, _raw: bool) -> IoResult<()> { - Err(super::unimpl()) - } - fn get_winsize(&mut self) -> IoResult<(int, int)> { - Err(super::unimpl()) - } - fn isatty(&self) -> bool { false } -} - -impl Drop for Inner { +impl Drop for FileDesc { fn drop(&mut self) { // closing stdio file handles makes no sense, so never do it. Also, note // that errors are ignored when closing a file descriptor. The reason @@ -251,39 +164,26 @@ impl Drop for Inner { } } -pub fn to_utf16(s: &CString) -> IoResult> { - match s.as_str() { - Some(s) => Ok({ - let mut s = s.utf16_units().collect::>(); - s.push(0); - s - }), - None => Err(IoError { - code: libc::ERROR_INVALID_NAME as uint, - extra: 0, - detail: Some("valid unicode input required".to_string()), - }) - } +pub fn to_utf16(s: &Path) -> IoResult> { + sys::to_utf16(s.as_str()) } -pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess) - -> IoResult { +pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult { // Flags passed to open_osfhandle let flags = match fm { - rtio::Open => 0, - rtio::Append => libc::O_APPEND, - rtio::Truncate => libc::O_TRUNC, + Open => 0, + Append => libc::O_APPEND, + Truncate => libc::O_TRUNC, }; let flags = match fa { - rtio::Read => flags | libc::O_RDONLY, - rtio::Write => flags | libc::O_WRONLY | libc::O_CREAT, - rtio::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT, + Read => flags | libc::O_RDONLY, + Write => flags | libc::O_WRONLY | libc::O_CREAT, + ReadWrite => flags | libc::O_RDWR | libc::O_CREAT, }; - let mut dwDesiredAccess = match fa { - rtio::Read => libc::FILE_GENERIC_READ, - rtio::Write => libc::FILE_GENERIC_WRITE, - rtio::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE + Read => libc::FILE_GENERIC_READ, + Write => libc::FILE_GENERIC_WRITE, + ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE }; // libuv has a good comment about this, but the basic idea is what we try to @@ -293,15 +193,15 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess) libc::FILE_SHARE_DELETE; let dwCreationDisposition = match (fm, fa) { - (rtio::Truncate, rtio::Read) => libc::TRUNCATE_EXISTING, - (rtio::Truncate, _) => libc::CREATE_ALWAYS, - (rtio::Open, rtio::Read) => libc::OPEN_EXISTING, - (rtio::Open, _) => libc::OPEN_ALWAYS, - (rtio::Append, rtio::Read) => { + (Truncate, Read) => libc::TRUNCATE_EXISTING, + (Truncate, _) => libc::CREATE_ALWAYS, + (Open, Read) => libc::OPEN_EXISTING, + (Open, _) => libc::OPEN_ALWAYS, + (Append, Read) => { dwDesiredAccess |= libc::FILE_APPEND_DATA; libc::OPEN_EXISTING } - (rtio::Append, _) => { + (Append, _) => { dwDesiredAccess &= !libc::FILE_WRITE_DATA; dwDesiredAccess |= libc::FILE_APPEND_DATA; libc::OPEN_ALWAYS @@ -337,7 +237,7 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess) } } -pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> { +pub fn mkdir(p: &Path, _mode: uint) -> IoResult<()> { let p = try!(to_utf16(p)); super::mkerr_winbool(unsafe { // FIXME: turn mode into something useful? #2623 @@ -345,20 +245,15 @@ pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> { }) } -pub fn readdir(p: &CString) -> IoResult> { - fn prune(root: &CString, dirs: Vec) -> Vec { - let root = unsafe { CString::new(root.as_ptr(), false) }; - let root = Path::new(root); - +pub fn readdir(p: &Path) -> IoResult> { + fn prune(root: &Path, dirs: Vec) -> Vec { dirs.into_iter().filter(|path| { path.as_vec() != b"." && path.as_vec() != b".." - }).map(|path| root.join(path).to_c_str()).collect() + }).map(|path| root.join(path)).collect() } - let star = Path::new(unsafe { - CString::new(p.as_ptr(), false) - }).join("*"); - let path = try!(to_utf16(&star.to_c_str())); + let star = p.join("*"); + let path = try!(to_utf16(&star)); unsafe { let mut wfd = mem::zeroed(); @@ -374,8 +269,8 @@ pub fn readdir(p: &CString) -> IoResult> { None => { assert!(libc::FindClose(find_handle) != 0); return Err(IoError { - code: super::c::ERROR_ILLEGAL_CHARACTER as uint, - extra: 0, + kind: io::InvalidInput, + desc: "path was not valid UTF-16", detail: Some(format!("path was not valid UTF-16: {}", filename)), }) }, // FIXME #12056: Convert the UCS-2 to invalid utf-8 instead of erroring @@ -391,42 +286,74 @@ pub fn readdir(p: &CString) -> IoResult> { } } -pub fn unlink(p: &CString) -> IoResult<()> { - let p = try!(to_utf16(p)); - super::mkerr_winbool(unsafe { - libc::DeleteFileW(p.as_ptr()) - }) +pub fn unlink(p: &Path) -> IoResult<()> { + fn do_unlink(p_utf16: &Vec) -> IoResult<()> { + super::mkerr_winbool(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) }) + } + + let p_utf16 = try!(to_utf16(p)); + let res = do_unlink(&p_utf16); + match res { + Ok(()) => Ok(()), + Err(e) => { + // FIXME: change the code below to use more direct calls + // than `stat` and `chmod`, to avoid re-conversion to + // utf16 etc. + + // On unix, a readonly file can be successfully removed. On windows, + // however, it cannot. To keep the two platforms in line with + // respect to their behavior, catch this case on windows, attempt to + // change it to read-write, and then remove the file. + if e.kind == io::PermissionDenied { + let stat = match stat(p) { + Ok(stat) => stat, + Err(..) => return Err(e), + }; + if stat.perm.intersects(io::USER_WRITE) { return Err(e) } + + match chmod(p, (stat.perm | io::USER_WRITE).bits() as uint) { + Ok(()) => do_unlink(&p_utf16), + Err(..) => { + // Try to put it back as we found it + let _ = chmod(p, stat.perm.bits() as uint); + Err(e) + } + } + } else { + Err(e) + } + } + } } -pub fn rename(old: &CString, new: &CString) -> IoResult<()> { +pub fn rename(old: &Path, new: &Path) -> IoResult<()> { let old = try!(to_utf16(old)); let new = try!(to_utf16(new)); super::mkerr_winbool(unsafe { - libc::MoveFileExW(old.as_ptr(), new.as_ptr(), - libc::MOVEFILE_REPLACE_EXISTING) + libc::MoveFileExW(old.as_ptr(), new.as_ptr(), libc::MOVEFILE_REPLACE_EXISTING) }) } -pub fn chmod(p: &CString, mode: uint) -> IoResult<()> { +pub fn chmod(p: &Path, mode: uint) -> IoResult<()> { let p = try!(to_utf16(p)); - super::mkerr_libc(unsafe { + mkerr_libc(unsafe { libc::wchmod(p.as_ptr(), mode as libc::c_int) }) } -pub fn rmdir(p: &CString) -> IoResult<()> { +pub fn rmdir(p: &Path) -> IoResult<()> { let p = try!(to_utf16(p)); - super::mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) }) + mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) }) } -pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> { +pub fn chown(_p: &Path, _uid: int, _gid: int) -> IoResult<()> { // libuv has this as a no-op, so seems like this should as well? Ok(()) } -pub fn readlink(p: &CString) -> IoResult { +pub fn readlink(p: &Path) -> IoResult { // FIXME: I have a feeling that this reads intermediate symlinks as well. - use io::c::compat::kernel32::GetFinalPathNameByHandleW; + use sys::c::compat::kernel32::GetFinalPathNameByHandleW; let p = try!(to_utf16(p)); let handle = unsafe { libc::CreateFileW(p.as_ptr(), @@ -449,18 +376,18 @@ pub fn readlink(p: &CString) -> IoResult { libc::VOLUME_NAME_DOS) }); let ret = match ret { - Some(ref s) if s.as_slice().starts_with(r"\\?\") => { - Ok(Path::new(s.as_slice().slice_from(4)).to_c_str()) + Some(ref s) if s.as_slice().starts_with(r"\\?\") => { // " + Ok(Path::new(s.as_slice().slice_from(4))) } - Some(s) => Ok(Path::new(s).to_c_str()), + Some(s) => Ok(Path::new(s)), None => Err(super::last_error()), }; assert!(unsafe { libc::CloseHandle(handle) } != 0); return ret; } -pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> { - use io::c::compat::kernel32::CreateSymbolicLinkW; +pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { + use sys::c::compat::kernel32::CreateSymbolicLinkW; let src = try!(to_utf16(src)); let dst = try!(to_utf16(dst)); super::mkerr_winbool(unsafe { @@ -468,7 +395,7 @@ pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> { }) } -pub fn link(src: &CString, dst: &CString) -> IoResult<()> { +pub fn link(src: &Path, dst: &Path) -> IoResult<()> { let src = try!(to_utf16(src)); let dst = try!(to_utf16(dst)); super::mkerr_winbool(unsafe { @@ -476,28 +403,37 @@ pub fn link(src: &CString, dst: &CString) -> IoResult<()> { }) } -fn mkstat(stat: &libc::stat) -> rtio::FileStat { - rtio::FileStat { +fn mkstat(stat: &libc::stat) -> FileStat { + FileStat { size: stat.st_size as u64, - kind: stat.st_mode as u64, - perm: stat.st_mode as u64, + kind: match (stat.st_mode as libc::c_int) & libc::S_IFMT { + libc::S_IFREG => io::TypeFile, + libc::S_IFDIR => io::TypeDirectory, + libc::S_IFIFO => io::TypeNamedPipe, + libc::S_IFBLK => io::TypeBlockSpecial, + libc::S_IFLNK => io::TypeSymlink, + _ => io::TypeUnknown, + }, + perm: FilePermission::from_bits_truncate(stat.st_mode as u32), created: stat.st_ctime as u64, modified: stat.st_mtime as u64, accessed: stat.st_atime as u64, - device: stat.st_dev as u64, - inode: stat.st_ino as u64, - rdev: stat.st_rdev as u64, - nlink: stat.st_nlink as u64, - uid: stat.st_uid as u64, - gid: stat.st_gid as u64, - blksize: 0, - blocks: 0, - flags: 0, - gen: 0, + unstable: UnstableFileStat { + device: stat.st_dev as u64, + inode: stat.st_ino as u64, + rdev: stat.st_rdev as u64, + nlink: stat.st_nlink as u64, + uid: stat.st_uid as u64, + gid: stat.st_gid as u64, + blksize:0, + blocks: 0, + flags: 0, + gen: 0, + }, } } -pub fn stat(p: &CString) -> IoResult { +pub fn stat(p: &Path) -> IoResult { let mut stat: libc::stat = unsafe { mem::zeroed() }; let p = try!(to_utf16(p)); match unsafe { libc::wstat(p.as_ptr(), &mut stat) } { @@ -506,18 +442,19 @@ pub fn stat(p: &CString) -> IoResult { } } -pub fn lstat(_p: &CString) -> IoResult { +// FIXME: move this to platform-specific modules (for now)? +pub fn lstat(_p: &Path) -> IoResult { // FIXME: implementation is missing Err(super::unimpl()) } -pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> { +pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> { let mut buf = libc::utimbuf { actime: atime as libc::time64_t, modtime: mtime as libc::time64_t, }; let p = try!(to_utf16(p)); - super::mkerr_libc(unsafe { + mkerr_libc(unsafe { libc::wutime(p.as_ptr(), &mut buf) }) }