From 68d5510292ed2f9714568285759ca9ef54d9b48c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 13 Nov 2013 14:48:45 -0800 Subject: [PATCH] Implement more native file I/O This implements a fair amount of the unimpl() functionality in io::native relating to filesystem operations. I've also modified all io::fs tests to run in both a native and uv environment (so everything is actually tested). There are a two bits of remaining functionality which I was unable to get working: * change_file_times on windows * lstat on windows I think that change_file_times may just need a better interface, but lstat has a large implementation in libuv which I didn't want to tackle trying to copy. --- src/libstd/io/buffered.rs | 1 - src/libstd/io/fs.rs | 317 +++--- src/libstd/io/native/file.rs | 1176 +++++++++++++--------- src/libstd/io/native/mod.rs | 90 +- src/libstd/io/stdio.rs | 12 +- src/libstd/libc.rs | 195 +++- src/rt/rust_builtin.c | 56 -- src/rt/rustrt.def.in | 7 - src/test/run-pass/conditional-compile.rs | 4 +- 9 files changed, 1099 insertions(+), 759 deletions(-) diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 2d8f1a72166..d74acb5f59b 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -391,7 +391,6 @@ mod test { // newtype struct autoderef weirdness #[test] fn test_buffered_stream() { - use rt; struct S; impl io::Writer for S { diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index d5514eb14d3..cc3f8bacb24 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -191,7 +191,7 @@ impl File { /// /// This function will raise on the `io_error` condition on failure. pub fn fsync(&mut self) { - self.fd.fsync(); + self.fd.fsync().map_err(|e| io_error::cond.raise(e)); } /// This function is similar to `fsync`, except that it may not synchronize @@ -203,23 +203,23 @@ impl File { /// /// This function will raise on the `io_error` condition on failure. pub fn datasync(&mut self) { - self.fd.datasync(); + self.fd.datasync().map_err(|e| io_error::cond.raise(e)); } - /// Either truncates or extends the underlying file, as extended from the - /// file's current position. This is equivalent to the unix `truncate` + /// Either truncates or extends the underlying file, updating the size of + /// this file to become `size`. This is equivalent to unix's `truncate` /// function. /// - /// The offset given is added to the file's current position and the result - /// is the new size of the file. If the new size is less than the current - /// size, then the file is truncated. If the new size is greater than the - /// current size, then the file is expanded to be filled with 0s. + /// If the `size` is less than the current file's size, then the file will + /// be shrunk. If it is greater than the current file's size, then the file + /// will be extended to `size` and have all of the intermediate data filled + /// in with 0s. /// /// # Errors /// /// On error, this function will raise on the `io_error` condition. - pub fn truncate(&mut self, offset: i64) { - self.fd.truncate(offset); + pub fn truncate(&mut self, size: i64) { + self.fd.truncate(size).map_err(|e| io_error::cond.raise(e)); } } @@ -715,29 +715,68 @@ impl path::Path { } #[cfg(test)] +#[allow(unused_imports)] mod test { use prelude::*; - use io::{SeekSet, SeekCur, SeekEnd, io_error, Read, Open, ReadWrite}; + use io::{SeekSet, SeekCur, SeekEnd, io_error, Read, Open, + ReadWrite}; use io; use str; - use super::{File, rmdir, mkdir, readdir, rmdir_recursive, mkdir_recursive, - copy, unlink, stat, symlink, link, readlink, chmod, - lstat, change_file_times}; + use io::fs::{File, rmdir, mkdir, readdir, rmdir_recursive, + mkdir_recursive, copy, unlink, stat, symlink, link, + readlink, chmod, lstat, change_file_times}; + use util; + use path::Path; + use io; + use ops::Drop; - fn tmpdir() -> Path { + struct TempDir(Path); + + impl Drop for TempDir { + fn drop(&mut self) { + // Gee, seeing how we're testing the fs module I sure hope that we + // at least implement this correctly! + io::fs::rmdir_recursive(&**self); + } + } + + fn tmpdir() -> TempDir { use os; use rand; let ret = os::tmpdir().join(format!("rust-{}", rand::random::())); - mkdir(&ret, io::UserRWX); - ret + io::fs::mkdir(&ret, io::UserRWX); + TempDir(ret) } - fn free(_: T) {} + macro_rules! test ( + { fn $name:ident() $b:block } => ( + mod $name { + use prelude::*; + use io::{SeekSet, SeekCur, SeekEnd, io_error, Read, Open, + ReadWrite}; + use io; + use str; + use io::fs::{File, rmdir, mkdir, readdir, rmdir_recursive, + mkdir_recursive, copy, unlink, stat, symlink, link, + readlink, chmod, lstat, change_file_times}; + use io::fs::test::tmpdir; + use util; - #[test] - fn file_test_io_smoke_test() { + fn f() $b + + #[test] fn uv() { f() } + #[test] fn native() { + use rt::test::run_in_newsched_task; + run_in_newsched_task(f); + } + } + ) + ) + + test!(fn file_test_io_smoke_test() { let message = "it's alright. have a good time"; - let filename = &Path::new("./tmp/file_rt_io_file_test.txt"); + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test.txt"); { let mut write_stream = File::open_mode(filename, Open, ReadWrite); write_stream.write(message.as_bytes()); @@ -752,11 +791,11 @@ mod test { assert!(read_str == message.to_owned()); } unlink(filename); - } + }) - #[test] - fn file_test_io_invalid_path_opened_without_create_should_raise_condition() { - let filename = &Path::new("./tmp/file_that_does_not_exist.txt"); + test!(fn invalid_path_raises() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_that_does_not_exist.txt"); let mut called = false; do io_error::cond.trap(|_| { called = true; @@ -765,11 +804,11 @@ mod test { assert!(result.is_none()); } assert!(called); - } + }) - #[test] - fn file_test_iounlinking_invalid_path_should_raise_condition() { - let filename = &Path::new("./tmp/file_another_file_that_does_not_exist.txt"); + test!(fn file_test_iounlinking_invalid_path_should_raise_condition() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); let mut called = false; do io_error::cond.trap(|_| { called = true; @@ -777,13 +816,13 @@ mod test { unlink(filename); } assert!(called); - } + }) - #[test] - fn file_test_io_non_positional_read() { + test!(fn file_test_io_non_positional_read() { let message = "ten-four"; let mut read_mem = [0, .. 8]; - let filename = &Path::new("./tmp/file_rt_io_file_test_positional.txt"); + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_positional.txt"); { let mut rw_stream = File::open_mode(filename, Open, ReadWrite); rw_stream.write(message.as_bytes()); @@ -802,16 +841,16 @@ mod test { unlink(filename); let read_str = str::from_utf8(read_mem); assert!(read_str == message.to_owned()); - } + }) - #[test] - fn file_test_io_seek_and_tell_smoke_test() { + test!(fn file_test_io_seek_and_tell_smoke_test() { let message = "ten-four"; let mut read_mem = [0, .. 4]; let set_cursor = 4 as u64; let mut tell_pos_pre_read; let mut tell_pos_post_read; - let filename = &Path::new("./tmp/file_rt_io_file_test_seeking.txt"); + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt"); { let mut rw_stream = File::open_mode(filename, Open, ReadWrite); rw_stream.write(message.as_bytes()); @@ -828,16 +867,16 @@ mod test { assert!(read_str == message.slice(4, 8).to_owned()); assert!(tell_pos_pre_read == set_cursor); assert!(tell_pos_post_read == message.len() as u64); - } + }) - #[test] - fn file_test_io_seek_and_write() { + test!(fn file_test_io_seek_and_write() { let initial_msg = "food-is-yummy"; let overwrite_msg = "-the-bar!!"; let final_msg = "foo-the-bar!!"; let seek_idx = 3; let mut read_mem = [0, .. 13]; - let filename = &Path::new("./tmp/file_rt_io_file_test_seek_and_write.txt"); + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt"); { let mut rw_stream = File::open_mode(filename, Open, ReadWrite); rw_stream.write(initial_msg.as_bytes()); @@ -851,17 +890,17 @@ mod test { unlink(filename); let read_str = str::from_utf8(read_mem); assert!(read_str == final_msg.to_owned()); - } + }) - #[test] - fn file_test_io_seek_shakedown() { + test!(fn file_test_io_seek_shakedown() { use std::str; // 01234567890123 let initial_msg = "qwer-asdf-zxcv"; let chunk_one = "qwer"; let chunk_two = "asdf"; let chunk_three = "zxcv"; let mut read_mem = [0, .. 4]; - let filename = &Path::new("./tmp/file_rt_io_file_test_seek_shakedown.txt"); + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt"); { let mut rw_stream = File::open_mode(filename, Open, ReadWrite); rw_stream.write(initial_msg.as_bytes()); @@ -885,11 +924,11 @@ mod test { assert!(read_str == chunk_one.to_owned()); } unlink(filename); - } + }) - #[test] - fn file_test_stat_is_correct_on_is_file() { - let filename = &Path::new("./tmp/file_stat_correct_on_is_file.txt"); + test!(fn file_test_stat_is_correct_on_is_file() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); { let mut fs = File::open_mode(filename, Open, ReadWrite); let msg = "hw"; @@ -898,49 +937,49 @@ mod test { let stat_res = stat(filename); assert_eq!(stat_res.kind, io::TypeFile); unlink(filename); - } + }) - #[test] - fn file_test_stat_is_correct_on_is_dir() { - let filename = &Path::new("./tmp/file_stat_correct_on_is_dir"); + test!(fn file_test_stat_is_correct_on_is_dir() { + let tmpdir = tmpdir(); + let filename = &tmpdir.join("file_stat_correct_on_is_dir"); mkdir(filename, io::UserRWX); let stat_res = filename.stat(); assert!(stat_res.kind == io::TypeDirectory); rmdir(filename); - } + }) - #[test] - fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { - let dir = &Path::new("./tmp/fileinfo_false_on_dir"); + test!(fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("fileinfo_false_on_dir"); mkdir(dir, io::UserRWX); assert!(dir.is_file() == false); rmdir(dir); - } + }) - #[test] - fn file_test_fileinfo_check_exists_before_and_after_file_creation() { - let file = &Path::new("./tmp/fileinfo_check_exists_b_and_a.txt"); + test!(fn file_test_fileinfo_check_exists_before_and_after_file_creation() { + let tmpdir = tmpdir(); + let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); File::create(file).write(bytes!("foo")); assert!(file.exists()); unlink(file); assert!(!file.exists()); - } + }) - #[test] - fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { - let dir = &Path::new("./tmp/before_and_after_dir"); + test!(fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { + let tmpdir = tmpdir(); + let dir = &tmpdir.join("before_and_after_dir"); assert!(!dir.exists()); mkdir(dir, io::UserRWX); assert!(dir.exists()); assert!(dir.is_dir()); rmdir(dir); assert!(!dir.exists()); - } + }) - #[test] - fn file_test_directoryinfo_readdir() { + test!(fn file_test_directoryinfo_readdir() { use std::str; - let dir = &Path::new("./tmp/di_readdir"); + let tmpdir = tmpdir(); + let dir = &tmpdir.join("di_readdir"); mkdir(dir, io::UserRWX); let prefix = "foo"; for n in range(0,3) { @@ -966,15 +1005,13 @@ mod test { unlink(f); } rmdir(dir); - } + }) - #[test] - fn recursive_mkdir_slash() { + test!(fn recursive_mkdir_slash() { mkdir_recursive(&Path::new("/"), io::UserRWX); - } + }) - #[test] - fn unicode_path_is_dir() { + test!(fn unicode_path_is_dir() { assert!(Path::new(".").is_dir()); assert!(!Path::new("test/stdtest/fs.rs").is_dir()); @@ -990,12 +1027,9 @@ mod test { File::create(&filepath); // ignore return; touch only assert!(!filepath.is_dir()); assert!(filepath.exists()); + }) - rmdir_recursive(&tmpdir); - } - - #[test] - fn unicode_path_exists() { + test!(fn unicode_path_exists() { assert!(Path::new(".").exists()); assert!(!Path::new("test/nonexistent-bogus-path").exists()); @@ -1005,11 +1039,9 @@ mod test { mkdir(&unicode, io::UserRWX); assert!(unicode.exists()); assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); - rmdir_recursive(&tmpdir); - } + }) - #[test] - fn copy_file_does_not_exist() { + test!(fn copy_file_does_not_exist() { let from = Path::new("test/nonexistent-bogus-path"); let to = Path::new("test/other-bogus-path"); match io::result(|| copy(&from, &to)) { @@ -1019,10 +1051,9 @@ mod test { assert!(!to.exists()); } } - } + }) - #[test] - fn copy_file_ok() { + test!(fn copy_file_ok() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1033,23 +1064,19 @@ mod test { assert_eq!(contents.as_slice(), bytes!("hello")); assert_eq!(input.stat().perm, out.stat().perm); - rmdir_recursive(&tmpdir); - } + }) - #[test] - fn copy_file_dst_dir() { + test!(fn copy_file_dst_dir() { let tmpdir = tmpdir(); let out = tmpdir.join("out"); File::create(&out); - match io::result(|| copy(&out, &tmpdir)) { + match io::result(|| copy(&out, &*tmpdir)) { Ok(*) => fail!(), Err(*) => {} } - rmdir_recursive(&tmpdir); - } + }) - #[test] - fn copy_file_dst_exists() { + test!(fn copy_file_dst_exists() { let tmpdir = tmpdir(); let input = tmpdir.join("in"); let output = tmpdir.join("out"); @@ -1060,24 +1087,19 @@ mod test { assert_eq!(File::open(&output).read_to_end(), (bytes!("foo")).to_owned()); + }) - rmdir_recursive(&tmpdir); - } - - #[test] - fn copy_file_src_dir() { + test!(fn copy_file_src_dir() { let tmpdir = tmpdir(); let out = tmpdir.join("out"); - match io::result(|| copy(&tmpdir, &out)) { + match io::result(|| copy(&*tmpdir, &out)) { Ok(*) => fail!(), Err(*) => {} } assert!(!out.exists()); - rmdir_recursive(&tmpdir); - } + }) - #[test] - fn copy_file_preserves_perm_bits() { + test!(fn copy_file_preserves_perm_bits() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1089,56 +1111,51 @@ mod test { chmod(&input, io::UserFile); chmod(&out, io::UserFile); - rmdir_recursive(&tmpdir); - } + }) - #[test] - #[ignore(cfg(windows))] // FIXME(#10264) operation not permitted? - fn symlinks_work() { + #[cfg(not(windows))] // FIXME(#10264) operation not permitted? + test!(fn symlinks_work() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); File::create(&input).write("foobar".as_bytes()); symlink(&input, &out); - assert_eq!(lstat(&out).kind, io::TypeSymlink); + if cfg!(not(windows)) { + assert_eq!(lstat(&out).kind, io::TypeSymlink); + } assert_eq!(stat(&out).size, stat(&input).size); assert_eq!(File::open(&out).read_to_end(), (bytes!("foobar")).to_owned()); + }) - rmdir_recursive(&tmpdir); - } - - #[test] - #[ignore(cfg(windows))] // apparently windows doesn't like symlinks - fn symlink_noexist() { + #[cfg(not(windows))] // apparently windows doesn't like symlinks + test!(fn symlink_noexist() { let tmpdir = tmpdir(); // symlinks can point to things that don't exist symlink(&tmpdir.join("foo"), &tmpdir.join("bar")); assert!(readlink(&tmpdir.join("bar")).unwrap() == tmpdir.join("foo")); - rmdir_recursive(&tmpdir); - } + }) - #[test] - fn readlink_not_symlink() { + test!(fn readlink_not_symlink() { let tmpdir = tmpdir(); - match io::result(|| readlink(&tmpdir)) { + match io::result(|| readlink(&*tmpdir)) { Ok(*) => fail!("wanted a failure"), Err(*) => {} } - rmdir_recursive(&tmpdir); - } + }) - #[test] - fn links_work() { + test!(fn links_work() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); File::create(&input).write("foobar".as_bytes()); link(&input, &out); - assert_eq!(lstat(&out).kind, io::TypeFile); + if cfg!(not(windows)) { + assert_eq!(lstat(&out).kind, io::TypeFile); + assert_eq!(stat(&out).unstable.nlink, 2); + } assert_eq!(stat(&out).size, stat(&input).size); - assert_eq!(stat(&out).unstable.nlink, 2); assert_eq!(File::open(&out).read_to_end(), (bytes!("foobar")).to_owned()); // can't link to yourself @@ -1151,12 +1168,9 @@ mod test { Ok(*) => fail!("wanted a failure"), Err(*) => {} } + }) - rmdir_recursive(&tmpdir); - } - - #[test] - fn chmod_works() { + test!(fn chmod_works() { let tmpdir = tmpdir(); let file = tmpdir.join("in.txt"); @@ -1171,11 +1185,9 @@ mod test { } chmod(&file, io::UserFile); - rmdir_recursive(&tmpdir); - } + }) - #[test] - fn sync_doesnt_kill_anything() { + test!(fn sync_doesnt_kill_anything() { let tmpdir = tmpdir(); let path = tmpdir.join("in.txt"); @@ -1185,24 +1197,23 @@ mod test { file.write(bytes!("foo")); file.fsync(); file.datasync(); - free(file); + util::ignore(file); + }) - rmdir_recursive(&tmpdir); - } - - #[test] - fn truncate_works() { + test!(fn truncate_works() { let tmpdir = tmpdir(); let path = tmpdir.join("in.txt"); let mut file = File::open_mode(&path, io::Open, io::ReadWrite).unwrap(); file.write(bytes!("foo")); + file.fsync(); // Do some simple things with truncation assert_eq!(stat(&path).size, 3); file.truncate(10); assert_eq!(stat(&path).size, 10); file.write(bytes!("bar")); + file.fsync(); assert_eq!(stat(&path).size, 10); assert_eq!(File::open(&path).read_to_end(), (bytes!("foobar", 0, 0, 0, 0)).to_owned()); @@ -1213,16 +1224,14 @@ mod test { file.truncate(2); assert_eq!(stat(&path).size, 2); file.write(bytes!("wut")); + file.fsync(); assert_eq!(stat(&path).size, 9); assert_eq!(File::open(&path).read_to_end(), (bytes!("fo", 0, 0, 0, 0, "wut")).to_owned()); - free(file); + util::ignore(file); + }) - rmdir_recursive(&tmpdir); - } - - #[test] - fn open_flavors() { + test!(fn open_flavors() { let tmpdir = tmpdir(); match io::result(|| File::open_mode(&tmpdir.join("a"), io::Open, @@ -1258,9 +1267,7 @@ mod test { f.write("bar".as_bytes()); } assert_eq!(stat(&tmpdir.join("h")).size, 3); - - rmdir_recursive(&tmpdir); - } + }) #[test] fn utime() { @@ -1271,8 +1278,6 @@ mod test { change_file_times(&path, 1000, 2000); assert_eq!(path.stat().accessed, 1000); assert_eq!(path.stat().modified, 2000); - - rmdir_recursive(&tmpdir); } #[test] @@ -1283,7 +1288,5 @@ mod test { Ok(*) => fail!(), Err(*) => {} } - - rmdir_recursive(&tmpdir); } } diff --git a/src/libstd/io/native/file.rs b/src/libstd/io/native/file.rs index 5e39460ba6a..03d9dc7ef6e 100644 --- a/src/libstd/io/native/file.rs +++ b/src/libstd/io/native/file.rs @@ -12,16 +12,26 @@ #[allow(non_camel_case_types)]; +use c_str::CString; use io::IoError; use io; +use libc::c_int; use libc; use ops::Drop; use option::{Some, None, Option}; use os; +use path::{Path, GenericPath}; use ptr::RawPtr; use result::{Result, Ok, Err}; use rt::rtio; +use super::IoResult; +use unstable::intrinsics; use vec::ImmutableVector; +use vec; + +#[cfg(windows)] use os::win32::{as_utf16_p, fill_utf16_buf_and_decode}; +#[cfg(windows)] use ptr; +#[cfg(windows)] use str; fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 { #[cfg(windows)] static eintr: int = 0; // doesn't matter @@ -122,26 +132,157 @@ impl rtio::RtioFileStream for FileDesc { fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { self.inner_write(buf) } - fn pread(&mut self, _buf: &mut [u8], _offset: u64) -> Result { - Err(super::unimpl()) + fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result { + return os_pread(self.fd, vec::raw::to_ptr(buf), buf.len(), offset); + + #[cfg(windows)] + fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult { + unsafe { + let mut overlap: libc::OVERLAPPED = intrinsics::init(); + let handle = libc::get_osfhandle(fd) as libc::HANDLE; + let mut bytes_read = 0; + overlap.Offset = offset as libc::DWORD; + overlap.OffsetHigh = (offset >> 32) as libc::DWORD; + + match libc::ReadFile(handle, buf as libc::LPVOID, + amt as libc::DWORD, + &mut bytes_read, &mut overlap) { + 0 => Err(super::last_error()), + _ => Ok(bytes_read as int) + } + } + } + + #[cfg(unix)] + fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult { + match unsafe { + libc::pread(fd, buf as *libc::c_void, amt 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) -> Result<(), IoError> { - Err(super::unimpl()) + fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { + return os_pwrite(self.fd, vec::raw::to_ptr(buf), buf.len(), offset); + + #[cfg(windows)] + fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> { + unsafe { + let mut overlap: libc::OVERLAPPED = intrinsics::init(); + let handle = libc::get_osfhandle(fd) as libc::HANDLE; + overlap.Offset = offset as libc::DWORD; + overlap.OffsetHigh = (offset >> 32) as libc::DWORD; + + match libc::WriteFile(handle, buf as libc::LPVOID, + amt as libc::DWORD, + ptr::mut_null(), &mut overlap) { + 0 => Err(super::last_error()), + _ => Ok(()), + } + } + } + + #[cfg(unix)] + fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::pwrite(fd, buf as *libc::c_void, amt as libc::size_t, + offset as libc::off_t) + } as c_int) + } } - fn seek(&mut self, _pos: i64, _whence: io::SeekStyle) -> Result { - Err(super::unimpl()) + #[cfg(windows)] + fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result { + let whence = match style { + io::SeekSet => libc::FILE_BEGIN, + io::SeekEnd => libc::FILE_END, + io::SeekCur => libc::FILE_CURRENT, + }; + unsafe { + let handle = libc::get_osfhandle(self.fd) as libc::HANDLE; + let mut newpos = 0; + match libc::SetFilePointerEx(handle, pos, &mut newpos, whence) { + 0 => Err(super::last_error()), + _ => Ok(newpos as u64), + } + } + } + #[cfg(unix)] + fn seek(&mut self, pos: i64, whence: io::SeekStyle) -> Result { + let whence = match whence { + io::SeekSet => libc::SEEK_SET, + io::SeekEnd => libc::SEEK_END, + io::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) -> Result { - Err(super::unimpl()) + 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) -> Result<(), IoError> { - Err(super::unimpl()) + return os_fsync(self.fd); + + #[cfg(windows)] + fn os_fsync(fd: c_int) -> IoResult<()> { + super::mkerr_winbool(unsafe { + let handle = libc::get_osfhandle(fd); + libc::FlushFileBuffers(handle as libc::HANDLE) + }) + } + #[cfg(unix)] + fn os_fsync(fd: c_int) -> IoResult<()> { + super::mkerr_libc(unsafe { libc::fsync(fd) }) + } } + #[cfg(windows)] + fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); } + + #[cfg(not(windows))] fn datasync(&mut self) -> Result<(), IoError> { - Err(super::unimpl()) + return super::mkerr_libc(os_datasync(self.fd)); + + #[cfg(target_os = "macos")] + 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 { unsafe { libc::fdatasync(fd) } } + #[cfg(not(target_os = "macos"), not(target_os = "linux"))] + fn os_datasync(fd: c_int) -> c_int { unsafe { libc::fsync(fd) } } } - fn truncate(&mut self, _offset: i64) -> Result<(), IoError> { - Err(super::unimpl()) + + #[cfg(windows)] + fn truncate(&mut self, offset: i64) -> Result<(), IoError> { + let orig_pos = match self.tell() { Ok(i) => i, Err(e) => return Err(e) }; + match self.seek(offset, io::SeekSet) { + Ok(_) => {}, Err(e) => return Err(e), + }; + let ret = unsafe { + let handle = libc::get_osfhandle(self.fd) as libc::HANDLE; + match libc::SetEndOfFile(handle) { + 0 => Err(super::last_error()), + _ => Ok(()) + } + }; + self.seek(orig_pos as i64, io::SeekSet); + return ret; + } + #[cfg(unix)] + fn truncate(&mut self, offset: i64) -> Result<(), IoError> { + super::mkerr_libc(unsafe { + libc::ftruncate(self.fd, offset as libc::off_t) + }) } } @@ -179,7 +320,8 @@ impl Drop for FileDesc { } pub struct CFile { - priv file: *libc::FILE + priv file: *libc::FILE, + priv fd: FileDesc, } impl CFile { @@ -187,7 +329,16 @@ impl CFile { /// /// The `CFile` takes ownership of the `FILE` pointer and will close it upon /// destruction. - pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } } + pub fn new(file: *libc::FILE) -> CFile { + CFile { + file: file, + fd: FileDesc::new(unsafe { libc::fileno(file) }, false) + } + } + + pub fn flush(&mut self) -> Result<(), IoError> { + super::mkerr_libc(unsafe { libc::fflush(self.file) }) + } } impl rtio::RtioFileStream for CFile { @@ -221,11 +372,13 @@ impl rtio::RtioFileStream for CFile { } } - fn pread(&mut self, _buf: &mut [u8], _offset: u64) -> Result { - Err(super::unimpl()) + fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result { + self.flush(); + self.fd.pread(buf, offset) } - fn pwrite(&mut self, _buf: &[u8], _offset: u64) -> Result<(), IoError> { - Err(super::unimpl()) + fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { + self.flush(); + self.fd.pwrite(buf, offset) } fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result { let whence = match style { @@ -249,13 +402,16 @@ impl rtio::RtioFileStream for CFile { } } fn fsync(&mut self) -> Result<(), IoError> { - Err(super::unimpl()) + self.flush(); + self.fd.fsync() } fn datasync(&mut self) -> Result<(), IoError> { - Err(super::unimpl()) + self.flush(); + self.fd.fsync() } - fn truncate(&mut self, _offset: i64) -> Result<(), IoError> { - Err(super::unimpl()) + fn truncate(&mut self, offset: i64) -> Result<(), IoError> { + self.flush(); + self.fd.truncate(offset) } } @@ -265,13 +421,515 @@ impl Drop for CFile { } } +pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess) + -> IoResult { + let flags = match fm { + io::Open => 0, + io::Append => libc::O_APPEND, + io::Truncate => libc::O_TRUNC, + }; + // Opening with a write permission must silently create the file. + let (flags, mode) = match fa { + io::Read => (flags | libc::O_RDONLY, 0), + io::Write => (flags | libc::O_WRONLY | libc::O_CREAT, + libc::S_IRUSR | libc::S_IWUSR), + io::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT, + libc::S_IRUSR | libc::S_IWUSR), + }; + + return match os_open(path, flags, mode) { + -1 => Err(super::last_error()), + fd => Ok(FileDesc::new(fd, true)), + }; + + #[cfg(windows)] + fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int { + do as_utf16_p(path.as_str().unwrap()) |path| { + unsafe { libc::wopen(path, flags, mode) } + } + } + + #[cfg(unix)] + fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int { + unsafe { libc::open(path.with_ref(|p| p), flags, mode) } + } +} + +pub fn mkdir(p: &CString, mode: io::FilePermission) -> IoResult<()> { + return os_mkdir(p, mode as c_int); + + #[cfg(windows)] + fn os_mkdir(p: &CString, _mode: c_int) -> IoResult<()> { + super::mkerr_winbool(unsafe { + // FIXME: turn mode into something useful? #2623 + do as_utf16_p(p.as_str().unwrap()) |buf| { + libc::CreateDirectoryW(buf, ptr::mut_null()) + } + }) + } + + #[cfg(unix)] + fn os_mkdir(p: &CString, mode: c_int) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::mkdir(p.with_ref(|p| p), mode as libc::mode_t) + }) + } +} + +pub fn readdir(p: &CString) -> IoResult<~[Path]> { + fn prune(root: &CString, dirs: ~[Path]) -> ~[Path] { + let root = unsafe { CString::new(root.with_ref(|p| p), false) }; + let root = Path::new(root); + + dirs.move_iter().filter(|path| { + path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..") + }).map(|path| root.join(path)).collect() + } + + unsafe { + #[cfg(not(windows))] + unsafe fn get_list(p: &CString) -> IoResult<~[Path]> { + use libc::{dirent_t}; + use libc::{opendir, readdir, closedir}; + extern { + fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; + } + debug!("os::list_dir -- BEFORE OPENDIR"); + + let dir_ptr = do p.with_ref |buf| { + opendir(buf) + }; + + if (dir_ptr as uint != 0) { + let mut paths = ~[]; + debug!("os::list_dir -- opendir() SUCCESS"); + let mut entry_ptr = readdir(dir_ptr); + while (entry_ptr as uint != 0) { + let cstr = CString::new(rust_list_dir_val(entry_ptr), false); + paths.push(Path::new(cstr)); + entry_ptr = readdir(dir_ptr); + } + closedir(dir_ptr); + Ok(paths) + } else { + Err(super::last_error()) + } + } + + #[cfg(windows)] + unsafe fn get_list(p: &CString) -> IoResult<~[Path]> { + use libc::consts::os::extra::INVALID_HANDLE_VALUE; + use libc::{wcslen, free}; + use libc::funcs::extra::kernel32::{ + FindFirstFileW, + FindNextFileW, + FindClose, + }; + use libc::types::os::arch::extra::HANDLE; + use os::win32::{ + as_utf16_p + }; + use rt::global_heap::malloc_raw; + + #[nolink] + extern { + fn rust_list_dir_wfd_size() -> libc::size_t; + fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16; + } + let p = CString::new(p.with_ref(|p| p), false); + let p = Path::new(p); + let star = p.join("*"); + do as_utf16_p(star.as_str().unwrap()) |path_ptr| { + let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); + let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE); + if find_handle as libc::c_int != INVALID_HANDLE_VALUE { + let mut paths = ~[]; + let mut more_files = 1 as libc::c_int; + while more_files != 0 { + let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr); + if fp_buf as uint == 0 { + fail!("os::list_dir() failure: got null ptr from wfd"); + } + else { + let fp_vec = vec::from_buf( + fp_buf, wcslen(fp_buf) as uint); + let fp_str = str::from_utf16(fp_vec); + paths.push(Path::new(fp_str)); + } + more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); + } + FindClose(find_handle); + free(wfd_ptr); + Ok(paths) + } else { + Err(super::last_error()) + } + } + } + + get_list(p).map(|paths| prune(p, paths)) + } +} + +pub fn unlink(p: &CString) -> IoResult<()> { + return os_unlink(p); + + #[cfg(windows)] + fn os_unlink(p: &CString) -> IoResult<()> { + super::mkerr_winbool(unsafe { + do as_utf16_p(p.as_str().unwrap()) |buf| { + libc::DeleteFileW(buf) + } + }) + } + + #[cfg(unix)] + fn os_unlink(p: &CString) -> IoResult<()> { + super::mkerr_libc(unsafe { libc::unlink(p.with_ref(|p| p)) }) + } +} + +pub fn rename(old: &CString, new: &CString) -> IoResult<()> { + return os_rename(old, new); + + #[cfg(windows)] + fn os_rename(old: &CString, new: &CString) -> IoResult<()> { + super::mkerr_winbool(unsafe { + do as_utf16_p(old.as_str().unwrap()) |old| { + do as_utf16_p(new.as_str().unwrap()) |new| { + libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING) + } + } + }) + } + + #[cfg(unix)] + fn os_rename(old: &CString, new: &CString) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::rename(old.with_ref(|p| p), new.with_ref(|p| p)) + }) + } +} + +pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> { + return super::mkerr_libc(os_chmod(p, mode as c_int)); + + #[cfg(windows)] + fn os_chmod(p: &CString, mode: c_int) -> c_int { + unsafe { + do as_utf16_p(p.as_str().unwrap()) |p| { + libc::wchmod(p, mode) + } + } + } + + #[cfg(unix)] + fn os_chmod(p: &CString, mode: c_int) -> c_int { + unsafe { libc::chmod(p.with_ref(|p| p), mode as libc::mode_t) } + } +} + +pub fn rmdir(p: &CString) -> IoResult<()> { + return super::mkerr_libc(os_rmdir(p)); + + #[cfg(windows)] + fn os_rmdir(p: &CString) -> c_int { + unsafe { + do as_utf16_p(p.as_str().unwrap()) |p| { libc::wrmdir(p) } + } + } + + #[cfg(unix)] + fn os_rmdir(p: &CString) -> c_int { + unsafe { libc::rmdir(p.with_ref(|p| p)) } + } +} + +pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> { + return super::mkerr_libc(os_chown(p, uid, gid)); + + // libuv has this as a no-op, so seems like this should as well? + #[cfg(windows)] + fn os_chown(_p: &CString, _uid: int, _gid: int) -> c_int { 0 } + + #[cfg(unix)] + fn os_chown(p: &CString, uid: int, gid: int) -> c_int { + unsafe { + libc::chown(p.with_ref(|p| p), uid as libc::uid_t, + gid as libc::gid_t) + } + } +} + +pub fn readlink(p: &CString) -> IoResult { + return os_readlink(p); + + // XXX: I have a feeling that this reads intermediate symlinks as well. + #[cfg(windows)] + fn os_readlink(p: &CString) -> IoResult { + let handle = unsafe { + do as_utf16_p(p.as_str().unwrap()) |p| { + libc::CreateFileW(p, + libc::GENERIC_READ, + libc::FILE_SHARE_READ, + ptr::mut_null(), + libc::OPEN_EXISTING, + libc::FILE_ATTRIBUTE_NORMAL, + ptr::mut_null()) + } + }; + if handle == ptr::mut_null() { return Err(super::last_error()) } + let ret = do fill_utf16_buf_and_decode |buf, sz| { + unsafe { + libc::GetFinalPathNameByHandleW(handle, buf as *u16, sz, + libc::VOLUME_NAME_NT) + } + }; + let ret = match ret { + Some(s) => Ok(Path::new(s)), + None => Err(super::last_error()), + }; + unsafe { libc::CloseHandle(handle) }; + return ret; + + } + + #[cfg(unix)] + fn os_readlink(p: &CString) -> IoResult { + let p = p.with_ref(|p| p); + let mut len = unsafe { libc::pathconf(p, libc::_PC_NAME_MAX) }; + if len == -1 { + len = 1024; // XXX: read PATH_MAX from C ffi? + } + let mut buf = vec::with_capacity::(len as uint); + match unsafe { + libc::readlink(p, vec::raw::to_ptr(buf) as *mut libc::c_char, + len as libc::size_t) + } { + -1 => Err(super::last_error()), + n => { + assert!(n > 0); + unsafe { vec::raw::set_len(&mut buf, n as uint); } + Ok(Path::new(buf)) + } + } + } +} + +pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> { + return os_symlink(src, dst); + + #[cfg(windows)] + fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_winbool(do as_utf16_p(src.as_str().unwrap()) |src| { + do as_utf16_p(dst.as_str().unwrap()) |dst| { + unsafe { libc::CreateSymbolicLinkW(dst, src, 0) } + } + }) + } + + #[cfg(unix)] + fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::symlink(src.with_ref(|p| p), dst.with_ref(|p| p)) + }) + } +} + +pub fn link(src: &CString, dst: &CString) -> IoResult<()> { + return os_link(src, dst); + + #[cfg(windows)] + fn os_link(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_winbool(do as_utf16_p(src.as_str().unwrap()) |src| { + do as_utf16_p(dst.as_str().unwrap()) |dst| { + unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) } + } + }) + } + + #[cfg(unix)] + fn os_link(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_libc(unsafe { + libc::link(src.with_ref(|p| p), dst.with_ref(|p| p)) + }) + } +} + +#[cfg(windows)] +fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat { + let path = unsafe { CString::new(path.with_ref(|p| p), false) }; + + // FileStat times are in milliseconds + fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } + + let kind = match (stat.st_mode as 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, + }; + + io::FileStat { + path: Path::new(path), + size: stat.st_size as u64, + kind: kind, + perm: (stat.st_mode) as io::FilePermission & io::AllPermissions, + created: stat.st_ctime as u64, + modified: stat.st_mtime as u64, + accessed: stat.st_atime as u64, + unstable: io::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, + } + } +} + +#[cfg(unix)] +fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat { + let path = unsafe { CString::new(path.with_ref(|p| p), false) }; + + // FileStat times are in milliseconds + fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } + + let kind = match (stat.st_mode as 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, + }; + + #[cfg(not(target_os = "linux"), not(target_os = "android"))] + fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 } + #[cfg(target_os = "linux")] #[cfg(target_os = "android")] + fn flags(_stat: &libc::stat) -> u64 { 0 } + + #[cfg(not(target_os = "linux"), not(target_os = "android"))] + fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 } + #[cfg(target_os = "linux")] #[cfg(target_os = "android")] + fn gen(_stat: &libc::stat) -> u64 { 0 } + + io::FileStat { + path: Path::new(path), + size: stat.st_size as u64, + kind: kind, + perm: (stat.st_mode) as io::FilePermission & io::AllPermissions, + 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: io::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: &CString) -> IoResult { + return os_stat(p); + + #[cfg(windows)] + fn os_stat(p: &CString) -> IoResult { + let mut stat: libc::stat = unsafe { intrinsics::uninit() }; + do as_utf16_p(p.as_str().unwrap()) |up| { + match unsafe { libc::wstat(up, &mut stat) } { + 0 => Ok(mkstat(&stat, p)), + _ => Err(super::last_error()), + } + } + } + + #[cfg(unix)] + fn os_stat(p: &CString) -> IoResult { + let mut stat: libc::stat = unsafe { intrinsics::uninit() }; + match unsafe { libc::stat(p.with_ref(|p| p), &mut stat) } { + 0 => Ok(mkstat(&stat, p)), + _ => Err(super::last_error()), + } + } +} + +pub fn lstat(p: &CString) -> IoResult { + return os_lstat(p); + + // XXX: windows implementation is missing + #[cfg(windows)] + fn os_lstat(_p: &CString) -> IoResult { + Err(super::unimpl()) + } + + #[cfg(unix)] + fn os_lstat(p: &CString) -> IoResult { + let mut stat: libc::stat = unsafe { intrinsics::uninit() }; + match unsafe { libc::lstat(p.with_ref(|p| p), &mut stat) } { + 0 => Ok(mkstat(&stat, p)), + _ => Err(super::last_error()), + } + } +} + +pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> { + return super::mkerr_libc(os_utime(p, atime, mtime)); + + #[cfg(windows)] + fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int { + let buf = libc::utimbuf { + actime: (atime / 1000) as libc::time64_t, + modtime: (mtime / 1000) as libc::time64_t, + }; + unsafe { + do as_utf16_p(p.as_str().unwrap()) |p| { + libc::wutime(p, &buf) + } + } + } + + #[cfg(unix)] + fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int { + let buf = libc::utimbuf { + actime: (atime / 1000) as libc::time_t, + modtime: (mtime / 1000) as libc::time_t, + }; + unsafe { libc::utime(p.with_ref(|p| p), &buf) } + } +} + #[cfg(test)] mod tests { + use io::native::file::{CFile, FileDesc}; + use io::fs; + use io; use libc; use os; - use io::{io_error, SeekSet, Writer, Reader}; + use path::Path; + use rand; use result::Ok; - use super::{CFile, FileDesc}; + use rt::rtio::RtioFileStream; + + fn tmpdir() -> Path { + let ret = os::tmpdir().join(format!("rust-{}", rand::random::())); + fs::mkdir(&ret, io::UserRWX); + ret + } #[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer fn test_file_desc() { @@ -308,7 +966,7 @@ mod tests { file.write(bytes!("test")); let mut buf = [0u8, ..4]; - file.seek(0, SeekSet); + file.seek(0, io::SeekSet); match file.read(buf) { Ok(4) => { assert_eq!(buf[0], 't' as u8); @@ -321,473 +979,3 @@ mod tests { } } } - -// n.b. these functions were all part of the old `std::os` module. There's lots -// of fun little nuances that were taken care of by these functions, but -// they are all thread-blocking versions that are no longer desired (we now -// use a non-blocking event loop implementation backed by libuv). -// -// In theory we will have a thread-blocking version of the event loop (if -// desired), so these functions may just need to get adapted to work in -// those situtations. For now, I'm leaving the code around so it doesn't -// get bitrotted instantaneously. -mod old_os { - use prelude::*; - use libc::{size_t, c_void, c_int}; - use libc; - use vec; - - #[cfg(not(windows))] use c_str::CString; - #[cfg(not(windows))] use libc::fclose; - #[cfg(test)] #[cfg(windows)] use os; - #[cfg(test)] use rand; - #[cfg(windows)] use str; - #[cfg(windows)] use ptr; - - // On Windows, wide character version of function must be used to support - // unicode, so functions should be split into at least two versions, - // which are for Windows and for non-Windows, if necessary. - // See https://github.com/mozilla/rust/issues/9822 for more information. - - mod rustrt { - use libc::{c_char, c_int}; - use libc; - - extern { - pub fn rust_path_is_dir(path: *libc::c_char) -> c_int; - pub fn rust_path_exists(path: *libc::c_char) -> c_int; - } - - // Uses _wstat instead of stat. - #[cfg(windows)] - extern { - pub fn rust_path_is_dir_u16(path: *u16) -> c_int; - pub fn rust_path_exists_u16(path: *u16) -> c_int; - } - } - - /// Recursively walk a directory structure - pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool { - let r = list_dir(p); - r.iter().advance(|q| { - let path = &p.join(q); - f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p))) - }) - } - - #[cfg(unix)] - /// Indicates whether a path represents a directory - pub fn path_is_dir(p: &Path) -> bool { - unsafe { - do p.with_c_str |buf| { - rustrt::rust_path_is_dir(buf) != 0 as c_int - } - } - } - - - #[cfg(windows)] - pub fn path_is_dir(p: &Path) -> bool { - unsafe { - do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| { - rustrt::rust_path_is_dir_u16(buf) != 0 as c_int - } - } - } - - #[cfg(unix)] - /// Indicates whether a path exists - pub fn path_exists(p: &Path) -> bool { - unsafe { - do p.with_c_str |buf| { - rustrt::rust_path_exists(buf) != 0 as c_int - } - } - } - - #[cfg(windows)] - pub fn path_exists(p: &Path) -> bool { - unsafe { - do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| { - rustrt::rust_path_exists_u16(buf) != 0 as c_int - } - } - } - - /// Creates a directory at the specified path - pub fn make_dir(p: &Path, mode: c_int) -> bool { - return mkdir(p, mode); - - #[cfg(windows)] - fn mkdir(p: &Path, _mode: c_int) -> bool { - unsafe { - use os::win32::as_utf16_p; - // FIXME: turn mode into something useful? #2623 - do as_utf16_p(p.as_str().unwrap()) |buf| { - libc::CreateDirectoryW(buf, ptr::mut_null()) - != (0 as libc::BOOL) - } - } - } - - #[cfg(unix)] - fn mkdir(p: &Path, mode: c_int) -> bool { - do p.with_c_str |buf| { - unsafe { - libc::mkdir(buf, mode as libc::mode_t) == (0 as c_int) - } - } - } - } - - /// Creates a directory with a given mode. - /// Returns true iff creation - /// succeeded. Also creates all intermediate subdirectories - /// if they don't already exist, giving all of them the same mode. - - // tjc: if directory exists but with different permissions, - // should we return false? - pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool { - if path_is_dir(p) { - return true; - } - if p.filename().is_some() { - let mut p_ = p.clone(); - p_.pop(); - if !mkdir_recursive(&p_, mode) { - return false; - } - } - return make_dir(p, mode); - } - - /// Lists the contents of a directory - /// - /// Each resulting Path is a relative path with no directory component. - pub fn list_dir(p: &Path) -> ~[Path] { - unsafe { - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - #[cfg(target_os = "freebsd")] - #[cfg(target_os = "macos")] - unsafe fn get_list(p: &Path) -> ~[Path] { - use libc::{dirent_t}; - use libc::{opendir, readdir, closedir}; - extern { - fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; - } - let mut paths = ~[]; - debug!("os::list_dir -- BEFORE OPENDIR"); - - let dir_ptr = do p.with_c_str |buf| { - opendir(buf) - }; - - if (dir_ptr as uint != 0) { - debug!("os::list_dir -- opendir() SUCCESS"); - let mut entry_ptr = readdir(dir_ptr); - while (entry_ptr as uint != 0) { - let cstr = CString::new(rust_list_dir_val(entry_ptr), false); - paths.push(Path::new(cstr)); - entry_ptr = readdir(dir_ptr); - } - closedir(dir_ptr); - } - else { - debug!("os::list_dir -- opendir() FAILURE"); - } - debug!("os::list_dir -- AFTER -- \\#: {}", paths.len()); - paths - } - #[cfg(windows)] - unsafe fn get_list(p: &Path) -> ~[Path] { - use libc::consts::os::extra::INVALID_HANDLE_VALUE; - use libc::{wcslen, free}; - use libc::funcs::extra::kernel32::{ - FindFirstFileW, - FindNextFileW, - FindClose, - }; - use libc::types::os::arch::extra::HANDLE; - use os::win32::{ - as_utf16_p - }; - use rt::global_heap::malloc_raw; - - #[nolink] - extern { - fn rust_list_dir_wfd_size() -> libc::size_t; - fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16; - } - let star = p.join("*"); - do as_utf16_p(star.as_str().unwrap()) |path_ptr| { - let mut paths = ~[]; - let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); - let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE); - if find_handle as libc::c_int != INVALID_HANDLE_VALUE { - let mut more_files = 1 as libc::c_int; - while more_files != 0 { - let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr); - if fp_buf as uint == 0 { - fail!("os::list_dir() failure: got null ptr from wfd"); - } - else { - let fp_vec = vec::from_buf( - fp_buf, wcslen(fp_buf) as uint); - let fp_str = str::from_utf16(fp_vec); - paths.push(Path::new(fp_str)); - } - more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); - } - FindClose(find_handle); - free(wfd_ptr) - } - paths - } - } - do get_list(p).move_iter().filter |path| { - path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..") - }.collect() - } - } - - /// Removes a directory at the specified path, after removing - /// all its contents. Use carefully! - pub fn remove_dir_recursive(p: &Path) -> bool { - let mut error_happened = false; - do walk_dir(p) |inner| { - if !error_happened { - if path_is_dir(inner) { - if !remove_dir_recursive(inner) { - error_happened = true; - } - } - else { - if !remove_file(inner) { - error_happened = true; - } - } - } - true - }; - // Directory should now be empty - !error_happened && remove_dir(p) - } - - /// Removes a directory at the specified path - pub fn remove_dir(p: &Path) -> bool { - return rmdir(p); - - #[cfg(windows)] - fn rmdir(p: &Path) -> bool { - unsafe { - use os::win32::as_utf16_p; - return do as_utf16_p(p.as_str().unwrap()) |buf| { - libc::RemoveDirectoryW(buf) != (0 as libc::BOOL) - }; - } - } - - #[cfg(unix)] - fn rmdir(p: &Path) -> bool { - do p.with_c_str |buf| { - unsafe { - libc::rmdir(buf) == (0 as c_int) - } - } - } - } - - /// Deletes an existing file - pub fn remove_file(p: &Path) -> bool { - return unlink(p); - - #[cfg(windows)] - fn unlink(p: &Path) -> bool { - unsafe { - use os::win32::as_utf16_p; - return do as_utf16_p(p.as_str().unwrap()) |buf| { - libc::DeleteFileW(buf) != (0 as libc::BOOL) - }; - } - } - - #[cfg(unix)] - fn unlink(p: &Path) -> bool { - unsafe { - do p.with_c_str |buf| { - libc::unlink(buf) == (0 as c_int) - } - } - } - } - - /// Renames an existing file or directory - pub fn rename_file(old: &Path, new: &Path) -> bool { - unsafe { - do old.with_c_str |old_buf| { - do new.with_c_str |new_buf| { - libc::rename(old_buf, new_buf) == (0 as c_int) - } - } - } - } - - /// Copies a file from one location to another - pub fn copy_file(from: &Path, to: &Path) -> bool { - return do_copy_file(from, to); - - #[cfg(windows)] - fn do_copy_file(from: &Path, to: &Path) -> bool { - unsafe { - use os::win32::as_utf16_p; - return do as_utf16_p(from.as_str().unwrap()) |fromp| { - do as_utf16_p(to.as_str().unwrap()) |top| { - libc::CopyFileW(fromp, top, (0 as libc::BOOL)) != - (0 as libc::BOOL) - } - } - } - } - - #[cfg(unix)] - fn do_copy_file(from: &Path, to: &Path) -> bool { - unsafe { - let istream = do from.with_c_str |fromp| { - do "rb".with_c_str |modebuf| { - libc::fopen(fromp, modebuf) - } - }; - if istream as uint == 0u { - return false; - } - // Preserve permissions - let from_mode = from.stat().perm; - - let ostream = do to.with_c_str |top| { - do "w+b".with_c_str |modebuf| { - libc::fopen(top, modebuf) - } - }; - if ostream as uint == 0u { - fclose(istream); - return false; - } - let bufsize = 8192u; - let mut buf = vec::with_capacity::(bufsize); - let mut done = false; - let mut ok = true; - while !done { - do buf.as_mut_buf |b, _sz| { - let nread = libc::fread(b as *mut c_void, 1u as size_t, - bufsize as size_t, - istream); - if nread > 0 as size_t { - if libc::fwrite(b as *c_void, 1u as size_t, nread, - ostream) != nread { - ok = false; - done = true; - } - } else { - done = true; - } - } - } - fclose(istream); - fclose(ostream); - - // Give the new file the old file's permissions - if do to.with_c_str |to_buf| { - libc::chmod(to_buf, from_mode as libc::mode_t) - } != 0 { - return false; // should be a condition... - } - return ok; - } - } - } - - #[test] - fn tmpdir() { - let p = os::tmpdir(); - let s = p.as_str(); - assert!(s.is_some() && s.unwrap() != "."); - } - - // Issue #712 - #[test] - fn test_list_dir_no_invalid_memory_access() { - list_dir(&Path::new(".")); - } - - #[test] - fn test_list_dir() { - let dirs = list_dir(&Path::new(".")); - // Just assuming that we've got some contents in the current directory - assert!(dirs.len() > 0u); - - for dir in dirs.iter() { - debug!("{:?}", (*dir).clone()); - } - } - - #[test] - #[cfg(not(windows))] - fn test_list_dir_root() { - let dirs = list_dir(&Path::new("/")); - assert!(dirs.len() > 1); - } - #[test] - #[cfg(windows)] - fn test_list_dir_root() { - let dirs = list_dir(&Path::new("C:\\")); - assert!(dirs.len() > 1); - } - - #[test] - fn test_path_is_dir() { - use io::fs::{mkdir_recursive}; - use io::{File, UserRWX}; - - assert!((path_is_dir(&Path::new(".")))); - assert!((!path_is_dir(&Path::new("test/stdtest/fs.rs")))); - - let mut dirpath = os::tmpdir(); - dirpath.push(format!("rust-test-{}/test-\uac00\u4e00\u30fc\u4f60\u597d", - rand::random::())); // 가一ー你好 - debug!("path_is_dir dirpath: {}", dirpath.display()); - - mkdir_recursive(&dirpath, UserRWX); - - assert!((path_is_dir(&dirpath))); - - let mut filepath = dirpath; - filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs"); - debug!("path_is_dir filepath: {}", filepath.display()); - - File::create(&filepath); // ignore return; touch only - assert!((!path_is_dir(&filepath))); - - assert!((!path_is_dir(&Path::new( - "test/unicode-bogus-dir-\uac00\u4e00\u30fc\u4f60\u597d")))); - } - - #[test] - fn test_path_exists() { - use io::fs::mkdir_recursive; - use io::UserRWX; - - assert!((path_exists(&Path::new(".")))); - assert!((!path_exists(&Path::new( - "test/nonexistent-bogus-path")))); - - let mut dirpath = os::tmpdir(); - dirpath.push(format!("rust-test-{}/test-\uac01\u4e01\u30fc\u518d\u89c1", - rand::random::())); // 각丁ー再见 - - mkdir_recursive(&dirpath, UserRWX); - assert!((path_exists(&dirpath))); - assert!((!path_exists(&Path::new( - "test/unicode-bogus-path-\uac01\u4e01\u30fc\u518d\u89c1")))); - } -} diff --git a/src/libstd/io/native/mod.rs b/src/libstd/io/native/mod.rs index d7aa6c14fcd..cec0de00ec2 100644 --- a/src/libstd/io/native/mod.rs +++ b/src/libstd/io/native/mod.rs @@ -90,6 +90,24 @@ fn last_error() -> IoError { } } +// unix has nonzero values as errors +fn mkerr_libc(ret: libc::c_int) -> IoResult<()> { + if ret != 0 { + Err(last_error()) + } else { + Ok(()) + } +} + +// windows has zero values as errors +fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> { + if ret == 0 { + Err(last_error()) + } else { + Ok(()) + } +} + /// Implementation of rt::rtio's IoFactory trait to generate handles to the /// native I/O functionality. pub struct IoFactory; @@ -125,51 +143,51 @@ impl rtio::IoFactory for IoFactory { }; ~file::FileDesc::new(fd, close) as ~RtioFileStream } - fn fs_open(&mut self, _path: &CString, _fm: io::FileMode, _fa: io::FileAccess) + fn fs_open(&mut self, path: &CString, fm: io::FileMode, fa: io::FileAccess) -> IoResult<~RtioFileStream> { - Err(unimpl()) + file::open(path, fm, fa).map(|fd| ~fd as ~RtioFileStream) } - fn fs_unlink(&mut self, _path: &CString) -> IoResult<()> { - Err(unimpl()) + fn fs_unlink(&mut self, path: &CString) -> IoResult<()> { + file::unlink(path) } - fn fs_stat(&mut self, _path: &CString) -> IoResult { - Err(unimpl()) + fn fs_stat(&mut self, path: &CString) -> IoResult { + file::stat(path) } - fn fs_mkdir(&mut self, _path: &CString, - _mode: io::FilePermission) -> IoResult<()> { - Err(unimpl()) + fn fs_mkdir(&mut self, path: &CString, + mode: io::FilePermission) -> IoResult<()> { + file::mkdir(path, mode) } - fn fs_chmod(&mut self, _path: &CString, - _mode: io::FilePermission) -> IoResult<()> { - Err(unimpl()) + fn fs_chmod(&mut self, path: &CString, + mode: io::FilePermission) -> IoResult<()> { + file::chmod(path, mode) } - fn fs_rmdir(&mut self, _path: &CString) -> IoResult<()> { - Err(unimpl()) + fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> { + file::rmdir(path) } - fn fs_rename(&mut self, _path: &CString, _to: &CString) -> IoResult<()> { - Err(unimpl()) + 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<~[Path]> { - Err(unimpl()) + fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<~[Path]> { + file::readdir(path) } - fn fs_lstat(&mut self, _path: &CString) -> IoResult { - Err(unimpl()) + fn fs_lstat(&mut self, path: &CString) -> IoResult { + file::lstat(path) } - fn fs_chown(&mut self, _path: &CString, _uid: int, _gid: int) -> IoResult<()> { - Err(unimpl()) + 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 { - Err(unimpl()) + fn fs_readlink(&mut self, path: &CString) -> IoResult { + file::readlink(path) } - fn fs_symlink(&mut self, _src: &CString, _dst: &CString) -> IoResult<()> { - Err(unimpl()) + fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> { + file::symlink(src, dst) } - fn fs_link(&mut self, _src: &CString, _dst: &CString) -> IoResult<()> { - Err(unimpl()) + 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<()> { - Err(unimpl()) + fn fs_utime(&mut self, src: &CString, atime: u64, + mtime: u64) -> IoResult<()> { + file::utime(src, atime, mtime) } // misc @@ -183,14 +201,18 @@ impl rtio::IoFactory for IoFactory { io.move_iter().map(|p| p.map(|p| ~p as ~RtioPipe)).collect()) }) } - fn pipe_open(&mut self, _fd: c_int) -> IoResult<~RtioPipe> { - Err(unimpl()) + fn pipe_open(&mut self, fd: c_int) -> IoResult<~RtioPipe> { + Ok(~file::FileDesc::new(fd, true) as ~RtioPipe) } fn tty_open(&mut self, fd: c_int, _readable: bool) -> IoResult<~RtioTTY> { if unsafe { libc::isatty(fd) } != 0 { Ok(~file::FileDesc::new(fd, true) as ~RtioTTY) } else { - Err(unimpl()) + Err(IoError { + kind: io::MismatchedFileTypeForOperation, + desc: "file descriptor is not a TTY", + detail: None, + }) } } fn signal(&mut self, _signal: Signum, _channel: SharedChan) diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 6df9ea6c60a..49cac428cb2 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -316,12 +316,22 @@ impl Writer for StdWriter { #[cfg(test)] mod tests { use super::*; + use rt::test::run_in_newsched_task; #[test] - fn smoke() { + fn smoke_uv() { // Just make sure we can acquire handles stdin(); stdout(); stderr(); } + + #[test] + fn smoke_native() { + do run_in_newsched_task { + stdin(); + stdout(); + stderr(); + } + } } diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs index 190f6a7c86f..b3528c5d3ab 100644 --- a/src/libstd/libc.rs +++ b/src/libstd/libc.rs @@ -329,6 +329,11 @@ pub mod types { __unused5: c_long, } + pub struct utimbuf { + actime: time_t, + modtime: time_t, + } + pub struct pthread_attr_t { __size: [u32, ..9] } @@ -365,6 +370,11 @@ pub mod types { st_ino: c_ulonglong } + pub struct utimbuf { + actime: time_t, + modtime: time_t, + } + pub struct pthread_attr_t { __size: [u32, ..9] } @@ -403,6 +413,11 @@ pub mod types { st_pad5: [c_long, ..14], } + pub struct utimbuf { + actime: time_t, + modtime: time_t, + } + pub struct pthread_attr_t { __size: [u32, ..9] } @@ -479,6 +494,11 @@ pub mod types { __unused: [c_long, ..3], } + pub struct utimbuf { + actime: time_t, + modtime: time_t, + } + pub struct pthread_attr_t { __size: [u64, ..7] } @@ -594,6 +614,11 @@ pub mod types { __unused: [uint8_t, ..2], } + pub struct utimbuf { + actime: time_t, + modtime: time_t, + } + pub type pthread_attr_t = *c_void; } pub mod posix08 { @@ -629,6 +654,12 @@ pub mod types { st_mtime: time64_t, st_ctime: time64_t, } + + // note that this is called utimbuf64 in win32 + pub struct utimbuf { + actime: time64_t, + modtime: time64_t, + } } } @@ -679,7 +710,7 @@ pub mod types { use libc::types::os::arch::c95::{c_char, c_int, c_uint, size_t}; use libc::types::os::arch::c95::{c_long, c_ulong}; use libc::types::os::arch::c95::{wchar_t}; - use libc::types::os::arch::c99::{c_ulonglong}; + use libc::types::os::arch::c99::{c_ulonglong, c_longlong}; pub type BOOL = c_int; pub type BYTE = u8; @@ -692,8 +723,13 @@ pub mod types { pub type HANDLE = LPVOID; pub type HMODULE = c_uint; + pub type LONG = c_long; + pub type PLONG = *mut c_long; pub type LONG_PTR = c_long; + pub type LARGE_INTEGER = c_longlong; + pub type PLARGE_INTEGER = *mut c_longlong; + pub type LPCWSTR = *WCHAR; pub type LPCSTR = *CHAR; pub type LPCTSTR = *CHAR; @@ -795,6 +831,16 @@ pub mod types { Type: DWORD } pub type LPMEMORY_BASIC_INFORMATION = *mut MEMORY_BASIC_INFORMATION; + + pub struct OVERLAPPED { + Internal: *c_ulong, + InternalHigh: *c_ulong, + Offset: DWORD, + OffsetHigh: DWORD, + hEvent: HANDLE, + } + + pub type LPOVERLAPPED = *mut OVERLAPPED; } } @@ -1065,6 +1111,11 @@ pub mod types { st_qspare: [int64_t, ..2], } + pub struct utimbuf { + actime: time_t, + modtime: time_t, + } + pub struct pthread_attr_t { __sig: c_long, __opaque: [c_char, ..36] @@ -1151,6 +1202,11 @@ pub mod types { st_qspare: [int64_t, ..2], } + pub struct utimbuf { + actime: time_t, + modtime: time_t, + } + pub struct pthread_attr_t { __sig: c_long, __opaque: [c_char, ..56] @@ -1337,6 +1393,72 @@ pub mod consts { pub static PROCESSOR_ARCHITECTURE_IA64 : WORD = 6; pub static PROCESSOR_ARCHITECTURE_AMD64 : WORD = 9; pub static PROCESSOR_ARCHITECTURE_UNKNOWN : WORD = 0xffff; + + pub static MOVEFILE_COPY_ALLOWED: DWORD = 2; + pub static MOVEFILE_CREATE_HARDLINK: DWORD = 16; + pub static MOVEFILE_DELAY_UNTIL_REBOOT: DWORD = 4; + pub static MOVEFILE_FAIL_IF_NOT_TRACKABLE: DWORD = 32; + pub static MOVEFILE_REPLACE_EXISTING: DWORD = 1; + pub static MOVEFILE_WRITE_THROUGH: DWORD = 8; + + pub static SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 1; + + pub static FILE_SHARE_DELETE: DWORD = 0x4; + pub static FILE_SHARE_READ: DWORD = 0x1; + pub static FILE_SHARE_WRITE: DWORD = 0x2; + + pub static CREATE_ALWAYS: DWORD = 2; + pub static CREATE_NEW: DWORD = 1; + pub static OPEN_ALWAYS: DWORD = 4; + pub static OPEN_EXISTING: DWORD = 3; + pub static TRUNCATE_EXISTING: DWORD = 5; + + pub static FILE_ATTRIBUTE_ARCHIVE: DWORD = 0x20; + pub static FILE_ATTRIBUTE_COMPRESSED: DWORD = 0x800; + pub static FILE_ATTRIBUTE_DEVICE: DWORD = 0x40; + pub static FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10; + pub static FILE_ATTRIBUTE_ENCRYPTED: DWORD = 0x4000; + pub static FILE_ATTRIBUTE_HIDDEN: DWORD = 0x2; + pub static FILE_ATTRIBUTE_INTEGRITY_STREAM: DWORD = 0x8000; + pub static FILE_ATTRIBUTE_NORMAL: DWORD = 0x80; + pub static FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: DWORD = 0x2000; + pub static FILE_ATTRIBUTE_NO_SCRUB_DATA: DWORD = 0x20000; + pub static FILE_ATTRIBUTE_OFFLINE: DWORD = 0x1000; + pub static FILE_ATTRIBUTE_READONLY: DWORD = 0x1; + pub static FILE_ATTRIBUTE_REPARSE_POINT: DWORD = 0x400; + pub static FILE_ATTRIBUTE_SPARSE_FILE: DWORD = 0x200; + pub static FILE_ATTRIBUTE_SYSTEM: DWORD = 0x4; + pub static FILE_ATTRIBUTE_TEMPORARY: DWORD = 0x100; + pub static FILE_ATTRIBUTE_VIRTUAL: DWORD = 0x10000; + + pub static FILE_FLAG_BACKUP_SEMANTICS: DWORD = 0x02000000; + pub static FILE_FLAG_DELETE_ON_CLOSE: DWORD = 0x04000000; + pub static FILE_FLAG_NO_BUFFERING: DWORD = 0x20000000; + pub static FILE_FLAG_OPEN_NO_RECALL: DWORD = 0x00100000; + pub static FILE_FLAG_OPEN_REPARSE_POINT: DWORD = 0x00200000; + pub static FILE_FLAG_OVERLAPPED: DWORD = 0x40000000; + pub static FILE_FLAG_POSIX_SEMANTICS: DWORD = 0x0100000; + pub static FILE_FLAG_RANDOM_ACCESS: DWORD = 0x10000000; + pub static FILE_FLAG_SESSION_AWARE: DWORD = 0x00800000; + pub static FILE_FLAG_SEQUENTIAL_SCAN: DWORD = 0x08000000; + pub static FILE_FLAG_WRITE_THROUGH: DWORD = 0x80000000; + + pub static FILE_NAME_NORMALIZED: DWORD = 0x0; + pub static FILE_NAME_OPENED: DWORD = 0x8; + + pub static VOLUME_NAME_DOS: DWORD = 0x0; + pub static VOLUME_NAME_GUID: DWORD = 0x1; + pub static VOLUME_NAME_NONE: DWORD = 0x4; + pub static VOLUME_NAME_NT: DWORD = 0x2; + + pub static GENERIC_READ: DWORD = 0x80000000; + pub static GENERIC_WRITE: DWORD = 0x40000000; + pub static GENERIC_EXECUTE: DWORD = 0x20000000; + pub static GENERIC_ALL: DWORD = 0x10000000; + + pub static FILE_BEGIN: DWORD = 0; + pub static FILE_CURRENT: DWORD = 1; + pub static FILE_END: DWORD = 2; } pub mod sysconf { } @@ -2873,18 +2995,26 @@ pub mod funcs { pub mod posix88 { #[nolink] pub mod stat_ { - use libc::types::os::common::posix01::stat; - use libc::types::os::arch::c95::{c_int, c_char}; + use libc::types::os::common::posix01::{stat, utimbuf}; + use libc::types::os::arch::c95::{c_int, c_char, wchar_t}; extern { #[link_name = "_chmod"] pub fn chmod(path: *c_char, mode: c_int) -> c_int; + #[link_name = "_wchmod"] + pub fn wchmod(path: *wchar_t, mode: c_int) -> c_int; #[link_name = "_mkdir"] pub fn mkdir(path: *c_char) -> c_int; + #[link_name = "_wrmdir"] + pub fn wrmdir(path: *wchar_t) -> c_int; #[link_name = "_fstat64"] pub fn fstat(fildes: c_int, buf: *mut stat) -> c_int; #[link_name = "_stat64"] pub fn stat(path: *c_char, buf: *mut stat) -> c_int; + #[link_name = "_wstat64"] + pub fn wstat(path: *wchar_t, buf: *mut stat) -> c_int; + #[link_name = "_wutime64"] + pub fn wutime(file: *wchar_t, buf: *utimbuf) -> c_int; } } @@ -2907,11 +3037,14 @@ pub mod funcs { #[nolink] pub mod fcntl { - use libc::types::os::arch::c95::{c_int, c_char}; + use libc::types::os::arch::c95::{c_int, c_char, wchar_t}; extern { #[link_name = "_open"] pub fn open(path: *c_char, oflag: c_int, mode: c_int) -> c_int; + #[link_name = "_wopen"] + pub fn wopen(path: *wchar_t, oflag: c_int, mode: c_int) + -> c_int; #[link_name = "_creat"] pub fn creat(path: *c_char, mode: c_int) -> c_int; } @@ -3079,9 +3212,12 @@ pub mod funcs { use libc::types::common::c95::c_void; use libc::types::os::arch::c95::{c_char, c_int, c_long, c_uint}; use libc::types::os::arch::c95::{size_t}; + use libc::types::os::arch::posix01::utimbuf; use libc::types::os::arch::posix88::{gid_t, off_t, pid_t}; use libc::types::os::arch::posix88::{ssize_t, uid_t}; + pub static _PC_NAME_MAX: c_int = 4; + extern { pub fn access(path: *c_char, amode: c_int) -> c_int; pub fn alarm(seconds: c_uint) -> c_uint; @@ -3130,6 +3266,11 @@ pub mod funcs { pub fn unlink(c: *c_char) -> c_int; pub fn write(fd: c_int, buf: *c_void, count: size_t) -> ssize_t; + pub fn pread(fd: c_int, buf: *c_void, count: size_t, + offset: off_t) -> ssize_t; + pub fn pwrite(fd: c_int, buf: *c_void, count: size_t, + offset: off_t) -> ssize_t; + pub fn utime(file: *c_char, buf: *utimbuf) -> c_int; } } @@ -3201,7 +3342,7 @@ pub mod funcs { #[nolink] pub mod unistd { use libc::types::os::arch::c95::{c_char, c_int, size_t}; - use libc::types::os::arch::posix88::{ssize_t}; + use libc::types::os::arch::posix88::{ssize_t, off_t}; extern { pub fn readlink(path: *c_char, @@ -3221,6 +3362,8 @@ pub mod funcs { pub fn putenv(string: *c_char) -> c_int; pub fn symlink(path1: *c_char, path2: *c_char) -> c_int; + + pub fn ftruncate(fd: c_int, length: off_t) -> c_int; } } @@ -3374,12 +3517,13 @@ pub mod funcs { use libc::types::os::arch::extra::{BOOL, DWORD, SIZE_T, HMODULE}; use libc::types::os::arch::extra::{LPCWSTR, LPWSTR, LPCTSTR, LPTSTR, LPTCH, LPDWORD, LPVOID, - LPCVOID}; + LPCVOID, LPOVERLAPPED}; use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, LPSTARTUPINFO, LPPROCESS_INFORMATION, LPMEMORY_BASIC_INFORMATION, LPSYSTEM_INFO}; - use libc::types::os::arch::extra::{HANDLE, LPHANDLE}; + use libc::types::os::arch::extra::{HANDLE, LPHANDLE, LARGE_INTEGER, + PLARGE_INTEGER}; extern "system" { pub fn GetEnvironmentVariableW(n: LPCWSTR, @@ -3486,6 +3630,43 @@ pub mod funcs { dwNumberOfBytesToMap: SIZE_T) -> LPVOID; pub fn UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL; + pub fn MoveFileExW(lpExistingFileName: LPCWSTR, + lpNewFileName: LPCWSTR, + dwFlags: DWORD) -> BOOL; + pub fn CreateSymbolicLinkW(lpSymlinkFileName: LPCWSTR, + lpTargetFileName: LPCWSTR, + dwFlags: DWORD) -> BOOL; + pub fn CreateHardLinkW(lpSymlinkFileName: LPCWSTR, + lpTargetFileName: LPCWSTR, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES) + -> BOOL; + pub fn FlushFileBuffers(hFile: HANDLE) -> BOOL; + pub fn CreateFileW(lpFileName: LPCWSTR, + dwDesiredAccess: DWORD, + dwShareMode: DWORD, + lpSecurityAttributes: LPSECURITY_ATTRIBUTES, + dwCreationDisposition: DWORD, + dwFlagsAndAttributes: DWORD, + hTemplateFile: HANDLE) -> HANDLE; + pub fn GetFinalPathNameByHandleW(hFile: HANDLE, + lpszFilePath: LPCWSTR, + cchFilePath: DWORD, + dwFlags: DWORD) -> DWORD; + pub fn ReadFile(hFile: HANDLE, + lpBuffer: LPVOID, + nNumberOfBytesToRead: DWORD, + lpNumberOfBytesRead: LPDWORD, + lpOverlapped: LPOVERLAPPED) -> BOOL; + pub fn WriteFile(hFile: HANDLE, + lpBuffer: LPVOID, + nNumberOfBytesToRead: DWORD, + lpNumberOfBytesRead: LPDWORD, + lpOverlapped: LPOVERLAPPED) -> BOOL; + pub fn SetFilePointerEx(hFile: HANDLE, + liDistanceToMove: LARGE_INTEGER, + lpNewFilePointer: PLARGE_INTEGER, + dwMoveMethod: DWORD) -> BOOL; + pub fn SetEndOfFile(hFile: HANDLE) -> BOOL; } } diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index c8ed61bfc41..c50bdc7c2c6 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -110,62 +110,6 @@ rust_list_dir_wfd_fp_buf(void* wfd) { } #endif -int -rust_path_is_dir(const char *path) { - struct stat buf; - if (stat(path, &buf)) { - return 0; - } - return S_ISDIR(buf.st_mode); -} - -int -#if defined(__WIN32__) -rust_path_is_dir_u16(const wchar_t *path) { - struct _stat buf; - // Don't use GetFileAttributesW, it cannot get attributes of - // some system files (e.g. pagefile.sys). - if (_wstat(path, &buf)) { - return 0; - } - return S_ISDIR(buf.st_mode); -} -#else -rust_path_is_dir_u16(const void *path) { - // Wide version of function is only used on Windows. - return 0; -} -#endif - -int -rust_path_exists(const char *path) { - struct stat buf; - if (stat(path, &buf)) { - return 0; - } - return 1; -} - -int -#if defined(__WIN32__) -rust_path_exists_u16(const wchar_t *path) { - struct _stat buf; - if (_wstat(path, &buf)) { - return 0; - } - return 1; -} -#else -rust_path_exists_u16(const void *path) { - // Wide version of function is only used on Windows. - return 0; -} -#endif - -FILE* rust_get_stdin() {return stdin;} -FILE* rust_get_stdout() {return stdout;} -FILE* rust_get_stderr() {return stderr;} - #if defined(__WIN32__) void rust_get_time(int64_t *sec, int32_t *nsec) { diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index c5b5e7f2c46..2a3e687ee55 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -9,13 +9,6 @@ rust_localtime rust_timegm rust_mktime rust_precise_time_ns -rust_path_is_dir -rust_path_is_dir_u16 -rust_path_exists -rust_path_exists_u16 -rust_get_stdin -rust_get_stdout -rust_get_stderr rust_list_dir_val rust_list_dir_wfd_size rust_list_dir_wfd_fp_buf diff --git a/src/test/run-pass/conditional-compile.rs b/src/test/run-pass/conditional-compile.rs index 89f4440f5bb..6b0f328629e 100644 --- a/src/test/run-pass/conditional-compile.rs +++ b/src/test/run-pass/conditional-compile.rs @@ -109,8 +109,8 @@ mod test_foreign_items { pub mod rustrt { extern { #[cfg(bogus)] - pub fn rust_get_stdin() -> ~str; - pub fn rust_get_stdin() -> ~str; + pub fn write() -> ~str; + pub fn write() -> ~str; } } }