From 843c5e6308920018defe62fd1951c8a5b45553b1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 26 Feb 2014 12:55:23 -0800 Subject: [PATCH 1/4] std: Small cleanup and test improvement This weeds out a bunch of warnings building stdtest on windows, and it also adds a check! macro to the io::fs tests to help diagnose errors that are cropping up on windows platforms as well. cc #12516 --- src/libstd/io/fs.rs | 287 ++++++++++++++++------------- src/libstd/io/net/unix.rs | 16 +- src/libstd/io/signal.rs | 23 ++- src/libstd/path/posix.rs | 4 +- src/libstd/path/windows.rs | 4 +- src/libstd/unstable/dynamic_lib.rs | 4 +- src/libstd/vec.rs | 2 +- 7 files changed, 182 insertions(+), 158 deletions(-) diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index ffccb0e8cb152..d72abb272c28d 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -663,6 +663,13 @@ mod test { use io; use ops::Drop; + macro_rules! check( ($e:expr) => ( + match $e { + Ok(t) => t, + Err(e) => fail!("{} failed with: {}", stringify!($e), e), + } + ) ) + struct TempDir(Path); impl TempDir { @@ -682,7 +689,7 @@ mod test { // Gee, seeing how we're testing the fs module I sure hope that we // at least implement this correctly! let TempDir(ref p) = *self; - io::fs::rmdir_recursive(p).unwrap(); + check!(io::fs::rmdir_recursive(p)); } } @@ -690,7 +697,7 @@ mod test { use os; use rand; let ret = os::tmpdir().join(format!("rust-{}", rand::random::())); - io::fs::mkdir(&ret, io::UserRWX).unwrap(); + check!(io::fs::mkdir(&ret, io::UserRWX)); TempDir(ret) } @@ -700,18 +707,18 @@ mod test { 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()).unwrap(); + check!(write_stream.write(message.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); let mut read_buf = [0, .. 1028]; - let read_str = match read_stream.read(read_buf).unwrap() { + let read_str = match check!(read_stream.read(read_buf)) { -1|0 => fail!("shouldn't happen"), - n => str::from_utf8_owned(read_buf.slice_to(n).to_owned()).unwrap() + n => str::from_utf8(read_buf.slice_to(n).to_owned()).unwrap().to_owned() }; assert_eq!(read_str, message.to_owned()); } - unlink(filename).unwrap(); + check!(unlink(filename)); }) iotest!(fn invalid_path_raises() { @@ -734,20 +741,20 @@ mod test { 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()).unwrap(); + check!(rw_stream.write(message.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); { let read_buf = read_mem.mut_slice(0, 4); - read_stream.read(read_buf).unwrap(); + check!(read_stream.read(read_buf)); } { let read_buf = read_mem.mut_slice(4, 8); - read_stream.read(read_buf).unwrap(); + check!(read_stream.read(read_buf)); } } - unlink(filename).unwrap(); + check!(unlink(filename)); let read_str = str::from_utf8(read_mem).unwrap(); assert_eq!(read_str, message); }) @@ -762,16 +769,16 @@ mod test { 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()).unwrap(); + check!(rw_stream.write(message.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); - read_stream.seek(set_cursor as i64, SeekSet).unwrap(); - tell_pos_pre_read = read_stream.tell().unwrap(); - read_stream.read(read_mem).unwrap(); - tell_pos_post_read = read_stream.tell().unwrap(); + check!(read_stream.seek(set_cursor as i64, SeekSet)); + tell_pos_pre_read = check!(read_stream.tell()); + check!(read_stream.read(read_mem)); + tell_pos_post_read = check!(read_stream.tell()); } - unlink(filename).unwrap(); + check!(unlink(filename)); let read_str = str::from_utf8(read_mem).unwrap(); assert_eq!(read_str, message.slice(4, 8)); assert_eq!(tell_pos_pre_read, set_cursor); @@ -788,15 +795,15 @@ mod test { 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()).unwrap(); - rw_stream.seek(seek_idx as i64, SeekSet).unwrap(); - rw_stream.write(overwrite_msg.as_bytes()).unwrap(); + check!(rw_stream.write(initial_msg.as_bytes())); + check!(rw_stream.seek(seek_idx as i64, SeekSet)); + check!(rw_stream.write(overwrite_msg.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); - read_stream.read(read_mem).unwrap(); + check!(read_stream.read(read_mem)); } - unlink(filename).unwrap(); + check!(unlink(filename)); let read_str = str::from_utf8(read_mem).unwrap(); assert!(read_str == final_msg.to_owned()); }) @@ -812,24 +819,24 @@ mod test { 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()).unwrap(); + check!(rw_stream.write(initial_msg.as_bytes())); } { let mut read_stream = File::open_mode(filename, Open, Read); - read_stream.seek(-4, SeekEnd).unwrap(); - read_stream.read(read_mem).unwrap(); + check!(read_stream.seek(-4, SeekEnd)); + check!(read_stream.read(read_mem)); assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_three); - read_stream.seek(-9, SeekCur).unwrap(); - read_stream.read(read_mem).unwrap(); + check!(read_stream.seek(-9, SeekCur)); + check!(read_stream.read(read_mem)); assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_two); - read_stream.seek(0, SeekSet).unwrap(); - read_stream.read(read_mem).unwrap(); + check!(read_stream.seek(0, SeekSet)); + check!(read_stream.read(read_mem)); assert_eq!(str::from_utf8(read_mem).unwrap(), chunk_one); } - unlink(filename).unwrap(); + check!(unlink(filename)); }) iotest!(fn file_test_stat_is_correct_on_is_file() { @@ -840,34 +847,34 @@ mod test { let msg = "hw"; fs.write(msg.as_bytes()).unwrap(); } - let stat_res = stat(filename).unwrap(); + let stat_res = check!(stat(filename)); assert_eq!(stat_res.kind, io::TypeFile); - unlink(filename).unwrap(); + check!(unlink(filename)); }) iotest!(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).unwrap(); - let stat_res = filename.stat().unwrap(); + check!(mkdir(filename, io::UserRWX)); + let stat_res = check!(filename.stat()); assert!(stat_res.kind == io::TypeDirectory); - rmdir(filename).unwrap(); + check!(rmdir(filename)); }) iotest!(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).unwrap(); + check!(mkdir(dir, io::UserRWX)); assert!(dir.is_file() == false); - rmdir(dir).unwrap(); + check!(rmdir(dir)); }) iotest!(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")).unwrap(); + check!(File::create(file).write(bytes!("foo"))); assert!(file.exists()); - unlink(file).unwrap(); + check!(unlink(file)); assert!(!file.exists()); }) @@ -875,10 +882,10 @@ mod test { let tmpdir = tmpdir(); let dir = &tmpdir.join("before_and_after_dir"); assert!(!dir.exists()); - mkdir(dir, io::UserRWX).unwrap(); + check!(mkdir(dir, io::UserRWX)); assert!(dir.exists()); assert!(dir.is_dir()); - rmdir(dir).unwrap(); + check!(rmdir(dir)); assert!(!dir.exists()); }) @@ -886,21 +893,21 @@ mod test { use std::str; let tmpdir = tmpdir(); let dir = &tmpdir.join("di_readdir"); - mkdir(dir, io::UserRWX).unwrap(); + check!(mkdir(dir, io::UserRWX)); let prefix = "foo"; for n in range(0,3) { let f = dir.join(format!("{}.txt", n)); - let mut w = File::create(&f).unwrap(); + let mut w = check!(File::create(&f)); let msg_str = (prefix + n.to_str().to_owned()).to_owned(); let msg = msg_str.as_bytes(); - w.write(msg).unwrap(); + check!(w.write(msg)); } - let files = readdir(dir).unwrap(); + let files = check!(readdir(dir)); let mut mem = [0u8, .. 4]; for f in files.iter() { { let n = f.filestem_str(); - File::open(f).read(mem).unwrap(); + check!(File::open(f).read(mem)); let read_str = str::from_utf8(mem).unwrap(); let expected = match n { None|Some("") => fail!("really shouldn't happen.."), @@ -908,13 +915,13 @@ mod test { }; assert_eq!(expected.as_slice(), read_str); } - unlink(f).unwrap(); + check!(unlink(f)); } - rmdir(dir).unwrap(); + check!(rmdir(dir)); }) iotest!(fn recursive_mkdir_slash() { - mkdir_recursive(&Path::new("/"), io::UserRWX).unwrap(); + check!(mkdir_recursive(&Path::new("/"), io::UserRWX)); }) iotest!(fn unicode_path_is_dir() { @@ -925,12 +932,12 @@ mod test { let mut dirpath = tmpdir.path().clone(); dirpath.push(format!("test-가一ー你好")); - mkdir(&dirpath, io::UserRWX).unwrap(); + check!(mkdir(&dirpath, io::UserRWX)); assert!(dirpath.is_dir()); let mut filepath = dirpath; filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs"); - File::create(&filepath).unwrap(); // ignore return; touch only + check!(File::create(&filepath)); // ignore return; touch only assert!(!filepath.is_dir()); assert!(filepath.exists()); }) @@ -942,7 +949,7 @@ mod test { let tmpdir = tmpdir(); let unicode = tmpdir.path(); let unicode = unicode.join(format!("test-각丁ー再见")); - mkdir(&unicode, io::UserRWX).unwrap(); + check!(mkdir(&unicode, io::UserRWX)); assert!(unicode.exists()); assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); }) @@ -964,19 +971,19 @@ mod test { let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); - File::create(&input).write(bytes!("hello")).unwrap(); - copy(&input, &out).unwrap(); - let contents = File::open(&out).read_to_end().unwrap(); + check!(File::create(&input).write(bytes!("hello"))); + check!(copy(&input, &out)); + let contents = check!(File::open(&out).read_to_end()); assert_eq!(contents.as_slice(), bytes!("hello")); - assert_eq!(input.stat().unwrap().perm, out.stat().unwrap().perm); + assert_eq!(check!(input.stat()).perm, check!(out.stat()).perm); }) iotest!(fn copy_file_dst_dir() { let tmpdir = tmpdir(); let out = tmpdir.join("out"); - File::create(&out).unwrap(); + check!(File::create(&out)); match copy(&out, tmpdir.path()) { Ok(..) => fail!(), Err(..) => {} } @@ -987,11 +994,11 @@ mod test { let input = tmpdir.join("in"); let output = tmpdir.join("out"); - File::create(&input).write("foo".as_bytes()).unwrap(); - File::create(&output).write("bar".as_bytes()).unwrap(); - copy(&input, &output).unwrap(); + check!(File::create(&input).write("foo".as_bytes())); + check!(File::create(&output).write("bar".as_bytes())); + check!(copy(&input, &output)); - assert_eq!(File::open(&output).read_to_end().unwrap(), + assert_eq!(check!(File::open(&output).read_to_end()), (bytes!("foo")).to_owned()); }) @@ -1010,13 +1017,13 @@ mod test { let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); - File::create(&input).unwrap(); - chmod(&input, io::UserRead).unwrap(); - copy(&input, &out).unwrap(); - assert!(out.stat().unwrap().perm & io::UserWrite == 0); + check!(File::create(&input)); + check!(chmod(&input, io::UserRead)); + check!(copy(&input, &out)); + assert!(check!(out.stat()).perm & io::UserWrite == 0); - chmod(&input, io::UserFile).unwrap(); - chmod(&out, io::UserFile).unwrap(); + check!(chmod(&input, io::UserFile)); + check!(chmod(&out, io::UserFile)); }) #[cfg(not(windows))] // FIXME(#10264) operation not permitted? @@ -1025,13 +1032,13 @@ mod test { let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); - File::create(&input).write("foobar".as_bytes()).unwrap(); - symlink(&input, &out).unwrap(); + check!(File::create(&input).write("foobar".as_bytes())); + check!(symlink(&input, &out)); if cfg!(not(windows)) { - assert_eq!(lstat(&out).unwrap().kind, io::TypeSymlink); + assert_eq!(check!(lstat(&out)).kind, io::TypeSymlink); } - assert_eq!(stat(&out).unwrap().size, stat(&input).unwrap().size); - assert_eq!(File::open(&out).read_to_end().unwrap(), + assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); + assert_eq!(check!(File::open(&out).read_to_end()), (bytes!("foobar")).to_owned()); }) @@ -1039,8 +1046,8 @@ mod test { iotest!(fn symlink_noexist() { let tmpdir = tmpdir(); // symlinks can point to things that don't exist - symlink(&tmpdir.join("foo"), &tmpdir.join("bar")).unwrap(); - assert!(readlink(&tmpdir.join("bar")).unwrap() == tmpdir.join("foo")); + check!(symlink(&tmpdir.join("foo"), &tmpdir.join("bar"))); + assert!(check!(readlink(&tmpdir.join("bar"))) == tmpdir.join("foo")); }) iotest!(fn readlink_not_symlink() { @@ -1056,14 +1063,14 @@ mod test { let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); - File::create(&input).write("foobar".as_bytes()).unwrap(); - link(&input, &out).unwrap(); + check!(File::create(&input).write("foobar".as_bytes())); + check!(link(&input, &out)); if cfg!(not(windows)) { - assert_eq!(lstat(&out).unwrap().kind, io::TypeFile); - assert_eq!(stat(&out).unwrap().unstable.nlink, 2); + assert_eq!(check!(lstat(&out)).kind, io::TypeFile); + assert_eq!(check!(stat(&out)).unstable.nlink, 2); } - assert_eq!(stat(&out).unwrap().size, stat(&input).unwrap().size); - assert_eq!(File::open(&out).read_to_end().unwrap(), + assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); + assert_eq!(check!(File::open(&out).read_to_end()), (bytes!("foobar")).to_owned()); // can't link to yourself @@ -1082,62 +1089,62 @@ mod test { let tmpdir = tmpdir(); let file = tmpdir.join("in.txt"); - File::create(&file).unwrap(); - assert!(stat(&file).unwrap().perm & io::UserWrite == io::UserWrite); - chmod(&file, io::UserRead).unwrap(); - assert!(stat(&file).unwrap().perm & io::UserWrite == 0); + check!(File::create(&file)); + assert!(check!(stat(&file)).perm & io::UserWrite == io::UserWrite); + check!(chmod(&file, io::UserRead)); + assert!(check!(stat(&file)).perm & io::UserWrite == 0); match chmod(&tmpdir.join("foo"), io::UserRWX) { Ok(..) => fail!("wanted a failure"), Err(..) => {} } - chmod(&file, io::UserFile).unwrap(); + check!(chmod(&file, io::UserFile)); }) iotest!(fn sync_doesnt_kill_anything() { let tmpdir = tmpdir(); let path = tmpdir.join("in.txt"); - let mut file = File::open_mode(&path, io::Open, io::ReadWrite).unwrap(); - file.fsync().unwrap(); - file.datasync().unwrap(); - file.write(bytes!("foo")).unwrap(); - file.fsync().unwrap(); - file.datasync().unwrap(); + let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite)); + check!(file.fsync()); + check!(file.datasync()); + check!(file.write(bytes!("foo"))); + check!(file.fsync()); + check!(file.datasync()); drop(file); - } #[ignore(cfg(windows))]) + }) iotest!(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")).unwrap(); - file.fsync().unwrap(); + let mut file = check!(File::open_mode(&path, io::Open, io::ReadWrite)); + check!(file.write(bytes!("foo"))); + check!(file.fsync()); // Do some simple things with truncation - assert_eq!(stat(&path).unwrap().size, 3); - file.truncate(10).unwrap(); - assert_eq!(stat(&path).unwrap().size, 10); - file.write(bytes!("bar")).unwrap(); - file.fsync().unwrap(); - assert_eq!(stat(&path).unwrap().size, 10); - assert_eq!(File::open(&path).read_to_end().unwrap(), + assert_eq!(check!(stat(&path)).size, 3); + check!(file.truncate(10)); + assert_eq!(check!(stat(&path)).size, 10); + check!(file.write(bytes!("bar"))); + check!(file.fsync()); + assert_eq!(check!(stat(&path)).size, 10); + assert_eq!(check!(File::open(&path).read_to_end()), (bytes!("foobar", 0, 0, 0, 0)).to_owned()); // Truncate to a smaller length, don't seek, and then write something. // Ensure that the intermediate zeroes are all filled in (we're seeked // past the end of the file). - file.truncate(2).unwrap(); - assert_eq!(stat(&path).unwrap().size, 2); - file.write(bytes!("wut")).unwrap(); - file.fsync().unwrap(); - assert_eq!(stat(&path).unwrap().size, 9); - assert_eq!(File::open(&path).read_to_end().unwrap(), + check!(file.truncate(2)); + assert_eq!(check!(stat(&path)).size, 2); + check!(file.write(bytes!("wut"))); + check!(file.fsync()); + assert_eq!(check!(stat(&path)).size, 9); + assert_eq!(check!(File::open(&path).read_to_end()), (bytes!("fo", 0, 0, 0, 0, "wut")).to_owned()); drop(file); - } #[ignore(cfg(windows))]) // FIXME(#11638) + }) iotest!(fn open_flavors() { let tmpdir = tmpdir(); @@ -1145,46 +1152,49 @@ mod test { match File::open_mode(&tmpdir.join("a"), io::Open, io::Read) { Ok(..) => fail!(), Err(..) => {} } - File::open_mode(&tmpdir.join("b"), io::Open, io::Write).unwrap(); - File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite).unwrap(); - File::open_mode(&tmpdir.join("d"), io::Append, io::Write).unwrap(); - File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite).unwrap(); - File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write).unwrap(); - File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite).unwrap(); - - File::create(&tmpdir.join("h")).write("foo".as_bytes()).unwrap(); - File::open_mode(&tmpdir.join("h"), io::Open, io::Read).unwrap(); + check!(File::open_mode(&tmpdir.join("b"), io::Open, io::Write)); + check!(File::open_mode(&tmpdir.join("c"), io::Open, io::ReadWrite)); + check!(File::open_mode(&tmpdir.join("d"), io::Append, io::Write)); + check!(File::open_mode(&tmpdir.join("e"), io::Append, io::ReadWrite)); + check!(File::open_mode(&tmpdir.join("f"), io::Truncate, io::Write)); + check!(File::open_mode(&tmpdir.join("g"), io::Truncate, io::ReadWrite)); + + check!(File::create(&tmpdir.join("h")).write("foo".as_bytes())); + check!(File::open_mode(&tmpdir.join("h"), io::Open, io::Read)); { - let mut f = File::open_mode(&tmpdir.join("h"), io::Open, - io::Read).unwrap(); + let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Open, + io::Read)); match f.write("wut".as_bytes()) { Ok(..) => fail!(), Err(..) => {} } } - assert_eq!(stat(&tmpdir.join("h")).unwrap().size, 3); + assert!(check!(stat(&tmpdir.join("h"))).size == 3, + "write/stat failed"); { - let mut f = File::open_mode(&tmpdir.join("h"), io::Append, - io::Write).unwrap(); - f.write("bar".as_bytes()).unwrap(); + let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Append, + io::Write)); + check!(f.write("bar".as_bytes())); } - assert_eq!(stat(&tmpdir.join("h")).unwrap().size, 6); + assert!(check!(stat(&tmpdir.join("h"))).size == 6, + "append didn't append"); { - let mut f = File::open_mode(&tmpdir.join("h"), io::Truncate, - io::Write).unwrap(); - f.write("bar".as_bytes()).unwrap(); + let mut f = check!(File::open_mode(&tmpdir.join("h"), io::Truncate, + io::Write)); + check!(f.write("bar".as_bytes())); } - assert_eq!(stat(&tmpdir.join("h")).unwrap().size, 3); + assert!(check!(stat(&tmpdir.join("h"))).size == 3, + "truncate didn't truncate"); }) #[test] fn utime() { let tmpdir = tmpdir(); let path = tmpdir.join("a"); - File::create(&path).unwrap(); + check!(File::create(&path)); - change_file_times(&path, 1000, 2000).unwrap(); - assert_eq!(path.stat().unwrap().accessed, 1000); - assert_eq!(path.stat().unwrap().modified, 2000); + check!(change_file_times(&path, 1000, 2000)); + assert_eq!(check!(path.stat()).accessed, 1000); + assert_eq!(check!(path.stat()).modified, 2000); } #[test] @@ -1196,4 +1206,17 @@ mod test { Err(..) => {} } } + + iotest!(fn binary_file() { + use rand::{rng, Rng}; + + let mut bytes = [0, ..1024]; + rng().fill_bytes(bytes); + + let tmpdir = tmpdir(); + + check!(File::create(&tmpdir.join("test")).write(bytes)); + let actual = check!(File::open(&tmpdir.join("test")).read_to_end()); + assert!(actual.as_slice == bytes); + }) } diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index c3e3d72c6721e..4545fea061b8f 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -137,16 +137,20 @@ mod tests { pub fn smalltest(server: proc(UnixStream), client: proc(UnixStream)) { let path1 = next_test_unix(); let path2 = path1.clone(); - let (port, chan) = Chan::new(); + + let mut acceptor = UnixListener::bind(&path1).listen(); spawn(proc() { - port.recv(); - client(UnixStream::connect(&path2).unwrap()); + match UnixStream::connect(&path2) { + Ok(c) => client(c), + Err(e) => fail!("failed connect: {}", e), + } }); - let mut acceptor = UnixListener::bind(&path1).listen(); - chan.send(()); - server(acceptor.accept().unwrap()); + match acceptor.accept() { + Ok(c) => server(c), + Err(e) => fail!("failed accept: {}", e), + } } iotest!(fn bind_error() { diff --git a/src/libstd/io/signal.rs b/src/libstd/io/signal.rs index 32670fa7c2c24..43419c751fcce 100644 --- a/src/libstd/io/signal.rs +++ b/src/libstd/io/signal.rs @@ -146,22 +146,20 @@ impl Listener { } } -#[cfg(test)] -mod test { +#[cfg(test, unix)] +mod test_unix { use libc; use comm::Empty; use io::timer; use super::{Listener, Interrupt}; - // kill is only available on Unixes - #[cfg(unix)] fn sigint() { unsafe { libc::funcs::posix88::signal::kill(libc::getpid(), libc::SIGINT); } } - #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378) + #[test] #[cfg(not(target_os="android"))] // FIXME(#10378) fn test_io_signal_smoketest() { let mut signal = Listener::new(); signal.register(Interrupt).unwrap(); @@ -173,7 +171,7 @@ mod test { } } - #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378) + #[test] #[cfg(not(target_os="android"))] // FIXME(#10378) fn test_io_signal_two_signal_one_signum() { let mut s1 = Listener::new(); let mut s2 = Listener::new(); @@ -191,7 +189,7 @@ mod test { } } - #[test] #[cfg(unix, not(target_os="android"))] // FIXME(#10378) + #[test] #[cfg(not(target_os="android"))] // FIXME(#10378) fn test_io_signal_unregister() { let mut s1 = Listener::new(); let mut s2 = Listener::new(); @@ -202,15 +200,16 @@ mod test { timer::sleep(10); assert_eq!(s2.port.try_recv(), Empty); } +} + +#[cfg(test, windows)] +mod test_windows { + use super::{User1, Listener}; + use result::{Ok, Err}; - #[cfg(windows)] #[test] fn test_io_signal_invalid_signum() { - use io; - use super::User1; - use result::{Ok, Err}; let mut s = Listener::new(); - let mut called = false; match s.register(User1) { Ok(..) => { fail!("Unexpected successful registry of signum {:?}", User1); diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index d1af612e0ec5e..a8f7782fa4623 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -698,8 +698,8 @@ mod tests { macro_rules! t( (s: $path:expr, $join:expr) => ( { - let path = ($path); - let join = ($join); + let path = $path; + let join = $join; let mut p1 = Path::new(path); let p2 = p1.clone(); p1.push(join); diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 8902ab2edd744..10834aec64c94 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -1433,8 +1433,8 @@ mod tests { macro_rules! t( (s: $path:expr, $join:expr) => ( { - let path = ($path); - let join = ($join); + let path = $path; + let join = $join; let mut p1 = Path::new(path); let p2 = p1.clone(); p1.push(join); diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs index 84fa528ebf130..57dbc045a65fe 100644 --- a/src/libstd/unstable/dynamic_lib.rs +++ b/src/libstd/unstable/dynamic_lib.rs @@ -82,9 +82,7 @@ impl DynamicLibrary { #[cfg(test)] mod test { use super::*; - use option::*; - use result::*; - use path::*; + use prelude::*; use libc; #[test] diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index cf49ea535623d..ce719c6d0b8db 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -3529,7 +3529,7 @@ mod tests { let mut v: [uint, .. 0] = []; v.sort(); - let mut v = [0xDEADBEEF]; + let mut v = [0xDEADBEEFu]; v.sort(); assert_eq!(v, [0xDEADBEEF]); } From cd9010c77e764e9348ecd92dc4a285f6514505dc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 26 Feb 2014 12:57:00 -0800 Subject: [PATCH 2/4] native: Improve windows file handling This commit splits the file implementation into file_unix and file_win32. The two implementations have diverged to the point that they share almost 0 code at this point, so it's easier to maintain as separate files. The other major change accompanied with this commit is that file::open is no longer based on libc's open function on windows, but rather windows's CreateFile function. This fixes dealing with binary files on windows (test added in previous commit). This also changes the read/write functions to use ReadFile and WriteFile instead of libc's read/write. Closes #12406 --- src/libnative/io/file.rs | 995 ------------------------------ src/libnative/io/file_unix.rs | 573 +++++++++++++++++ src/libnative/io/file_win32.rs | 516 ++++++++++++++++ src/libnative/io/mod.rs | 28 +- src/libnative/io/net.rs | 19 +- src/libnative/io/pipe_unix.rs | 4 +- src/libnative/io/timer_helper.rs | 3 - src/libnative/io/timer_other.rs | 2 - src/libnative/io/timer_timerfd.rs | 2 - src/libnative/lib.rs | 1 + src/libstd/libc.rs | 21 + 11 files changed, 1146 insertions(+), 1018 deletions(-) delete mode 100644 src/libnative/io/file.rs create mode 100644 src/libnative/io/file_unix.rs create mode 100644 src/libnative/io/file_win32.rs diff --git a/src/libnative/io/file.rs b/src/libnative/io/file.rs deleted file mode 100644 index 27430ddee97eb..0000000000000 --- a/src/libnative/io/file.rs +++ /dev/null @@ -1,995 +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 - -#[allow(non_camel_case_types)]; - -use std::sync::arc::UnsafeArc; -use std::c_str::CString; -use std::io::IoError; -use std::io; -use std::libc::{c_int, c_void}; -use std::libc; -use std::mem; -use std::os; -use std::rt::rtio; -use std::vec; - -use io::{IoResult, retry}; - -#[cfg(windows)] use std::os::win32::{as_utf16_p, fill_utf16_buf_and_decode}; -#[cfg(windows)] use std::ptr; -#[cfg(windows)] use std::str; - -pub fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 { - #[cfg(windows)] static eintr: int = 0; // doesn't matter - #[cfg(not(windows))] static eintr: int = libc::EINTR as int; - - let origamt = data.len(); - let mut data = data.as_ptr(); - let mut amt = origamt; - while amt > 0 { - let mut ret; - loop { - ret = f(data, amt); - if cfg!(windows) { break } // windows has no eintr - // if we get an eintr, then try again - if ret != -1 || os::errno() as int != eintr { break } - } - if ret == 0 { - break - } else if ret != -1 { - amt -= ret as uint; - data = unsafe { data.offset(ret as int) }; - } else { - return ret; - } - } - return (origamt - amt) as i64; -} - -pub type fd_t = libc::c_int; - -struct Inner { - fd: fd_t, - close_on_drop: bool, -} - -pub struct FileDesc { - priv inner: UnsafeArc -} - -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: UnsafeArc::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]) -> Result { - #[cfg(windows)] type rlen = libc::c_uint; - #[cfg(not(windows))] type rlen = libc::size_t; - let ret = retry(|| unsafe { - libc::read(self.fd(), - buf.as_mut_ptr() as *mut libc::c_void, - buf.len() as rlen) as libc::c_int - }); - if ret == 0 { - Err(io::standard_error(io::EndOfFile)) - } else if ret < 0 { - Err(super::last_error()) - } else { - Ok(ret as uint) - } - } - pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> { - #[cfg(windows)] type wlen = libc::c_uint; - #[cfg(not(windows))] type wlen = libc::size_t; - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::write(self.fd(), buf as *libc::c_void, len as wlen) as i64 - } - }); - if ret < 0 { - Err(super::last_error()) - } else { - Ok(()) - } - } - - pub fn fd(&self) -> fd_t { - // This unsafety is fine because we're just reading off the file - // descriptor, no one is modifying this. - unsafe { (*self.inner.get()).fd } - } -} - -impl io::Reader for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> io::IoResult { - self.inner_read(buf) - } -} - -impl io::Writer for FileDesc { - fn write(&mut self, buf: &[u8]) -> io::IoResult<()> { - self.inner_write(buf) - } -} - -impl rtio::RtioFileStream for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner_read(buf).map(|i| i as int) - } - fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { - self.inner_write(buf) - } - fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result { - return os_pread(self.fd(), buf.as_ptr(), buf.len(), offset); - - #[cfg(windows)] - fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult { - unsafe { - let mut overlap: libc::OVERLAPPED = mem::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 retry(|| unsafe { - libc::pread(fd, buf as *libc::c_void, amt as libc::size_t, - offset as libc::off_t) as libc::c_int - }) { - -1 => Err(super::last_error()), - n => Ok(n as int) - } - } - } - fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { - return os_pwrite(self.fd(), buf.as_ptr(), buf.len(), offset); - - #[cfg(windows)] - fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> { - unsafe { - let mut overlap: libc::OVERLAPPED = mem::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(retry(|| unsafe { - libc::pwrite(fd, buf as *libc::c_void, amt as libc::size_t, - offset as libc::off_t) - } as c_int)) - } - } - #[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 { - 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> { - 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(retry(|| unsafe { libc::fsync(fd) })) - } - } - #[cfg(windows)] - fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); } - - #[cfg(not(windows))] - fn datasync(&mut self) -> Result<(), IoError> { - 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 { - retry(|| unsafe { libc::fdatasync(fd) }) - } - #[cfg(not(target_os = "macos"), not(target_os = "linux"))] - fn os_datasync(fd: c_int) -> c_int { - retry(|| unsafe { libc::fsync(fd) }) - } - } - - #[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(()) - } - }; - let _ = self.seek(orig_pos as i64, io::SeekSet); - return ret; - } - #[cfg(unix)] - fn truncate(&mut self, offset: i64) -> Result<(), IoError> { - super::mkerr_libc(retry(|| unsafe { - libc::ftruncate(self.fd(), offset as libc::off_t) - })) - } -} - -impl rtio::RtioPipe for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner_read(buf) - } - fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { - self.inner_write(buf) - } - fn clone(&self) -> ~rtio::RtioPipe { - ~FileDesc { inner: self.inner.clone() } as ~rtio::RtioPipe - } -} - -impl rtio::RtioTTY for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner_read(buf) - } - fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { - self.inner_write(buf) - } - fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> { - Err(super::unimpl()) - } - fn get_winsize(&mut self) -> Result<(int, int), IoError> { - 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 { - warn!("error {} when closing file descriptor {}", n, self.fd); - } - } - } -} - -pub struct CFile { - priv file: *libc::FILE, - priv 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: *libc::FILE) -> CFile { - CFile { - file: file, - fd: FileDesc::new(unsafe { libc::fileno(file) }, false) - } - } - - pub fn flush(&mut self) -> Result<(), IoError> { - super::mkerr_libc(retry(|| unsafe { libc::fflush(self.file) })) - } -} - -impl rtio::RtioFileStream for CFile { - fn read(&mut self, buf: &mut [u8]) -> Result { - 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(io::standard_error(io::EndOfFile)) - } else if ret < 0 { - Err(super::last_error()) - } else { - Ok(ret as int) - } - } - - fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::fwrite(buf as *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) -> Result { - self.flush().and_then(|()| self.fd.pread(buf, offset)) - } - fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { - self.flush().and_then(|()| self.fd.pwrite(buf, offset)) - } - fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result { - let whence = match style { - io::SeekSet => libc::SEEK_SET, - io::SeekEnd => libc::SEEK_END, - io::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) -> Result { - let ret = unsafe { libc::ftell(self.file) }; - if ret < 0 { - Err(super::last_error()) - } else { - Ok(ret as u64) - } - } - fn fsync(&mut self) -> Result<(), IoError> { - self.flush().and_then(|()| self.fd.fsync()) - } - fn datasync(&mut self) -> Result<(), IoError> { - self.flush().and_then(|()| self.fd.fsync()) - } - fn truncate(&mut self, offset: i64) -> Result<(), IoError> { - self.flush().and_then(|()| self.fd.truncate(offset)) - } -} - -impl Drop for CFile { - fn drop(&mut self) { - unsafe { let _ = libc::fclose(self.file); } - } -} - -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 { - as_utf16_p(path.as_str().unwrap(), |path| { - retry(|| unsafe { libc::wopen(path, flags, mode) }) - }) - } - - #[cfg(unix)] - fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int { - retry(|| 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 - 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(retry(|| 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 std::libc::{dirent_t}; - use std::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 = 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); - } - assert_eq!(closedir(dir_ptr), 0); - Ok(paths) - } else { - Err(super::last_error()) - } - } - - #[cfg(windows)] - unsafe fn get_list(p: &CString) -> IoResult<~[Path]> { - use std::libc::consts::os::extra::INVALID_HANDLE_VALUE; - use std::libc::{wcslen, free}; - use std::libc::funcs::extra::kernel32::{ - FindFirstFileW, - FindNextFileW, - FindClose, - }; - use std::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("*"); - 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 as *c_void); - 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_trimmed = str::truncate_utf16_at_nul(fp_vec); - let fp_str = str::from_utf16(fp_trimmed) - .expect("rust_list_dir_wfd_fp_buf returned invalid UTF-16"); - paths.push(Path::new(fp_str)); - } - more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); - } - assert!(FindClose(find_handle) != 0); - free(wfd_ptr as *mut c_void); - 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 { - as_utf16_p(p.as_str().unwrap(), |buf| { - libc::DeleteFileW(buf) - }) - }) - } - - #[cfg(unix)] - fn os_unlink(p: &CString) -> IoResult<()> { - super::mkerr_libc(retry(|| 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 { - as_utf16_p(old.as_str().unwrap(), |old| { - 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(retry(|| 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 { - as_utf16_p(p.as_str().unwrap(), |p| retry(|| { - libc::wchmod(p, mode) - })) - } - } - - #[cfg(unix)] - fn os_chmod(p: &CString, mode: c_int) -> c_int { - retry(||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 { - as_utf16_p(p.as_str().unwrap(), |p| retry(|| { - libc::wrmdir(p) - })) - } - } - - #[cfg(unix)] - fn os_rmdir(p: &CString) -> c_int { - retry(|| 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 { - retry(|| 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); - - // FIXME: I have a feeling that this reads intermediate symlinks as well. - #[cfg(windows)] - fn os_readlink(p: &CString) -> IoResult { - let handle = unsafe { - 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 as int == libc::INVALID_HANDLE_VALUE as int { - return Err(super::last_error()) - } - let ret = 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()), - }; - assert!(unsafe { libc::CloseHandle(handle) } != 0); - 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; // FIXME: read PATH_MAX from C ffi? - } - let mut buf = vec::with_capacity::(len as uint); - match retry(|| 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: &CString, dst: &CString) -> IoResult<()> { - return os_symlink(src, dst); - - #[cfg(windows)] - fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> { - super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| { - as_utf16_p(dst.as_str().unwrap(), |dst| { - unsafe { libc::CreateSymbolicLinkW(dst, src, 0) } - }) as libc::BOOL - })) - } - - #[cfg(unix)] - fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> { - super::mkerr_libc(retry(|| 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(as_utf16_p(src.as_str().unwrap(), |src| { - 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(retry(|| 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) }; - 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 { mem::uninit() }; - as_utf16_p(p.as_str().unwrap(), |up| { - match retry(|| 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 { mem::uninit() }; - match retry(|| 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); - - // FIXME: 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 { mem::uninit() }; - match retry(|| 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 { - as_utf16_p(p.as_str().unwrap(), |p| retry(|| { - 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, - }; - retry(|| unsafe { libc::utime(p.with_ref(|p| p), &buf) }) - } -} - -#[cfg(test)] -mod tests { - use super::{CFile, FileDesc}; - use std::io; - use std::libc; - use std::os; - use std::rt::rtio::RtioFileStream; - - #[ignore(cfg(target_os = "freebsd"))] // 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. - unsafe { - let os::Pipe { input, out } = os::pipe(); - let mut reader = FileDesc::new(input, true); - let mut writer = FileDesc::new(out, true); - - writer.inner_write(bytes!("test")).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 => fail!("invalid read: {:?}", r) - } - - assert!(writer.inner_read(buf).is_err()); - assert!(reader.inner_write(buf).is_err()); - } - } - - #[ignore(cfg(windows))] // apparently windows doesn't like tmpfile - #[test] - fn test_cfile() { - unsafe { - let f = libc::tmpfile(); - assert!(!f.is_null()); - let mut file = CFile::new(f); - - file.write(bytes!("test")).unwrap(); - let mut buf = [0u8, ..4]; - let _ = file.seek(0, io::SeekSet).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 => fail!("invalid read: {:?}", r) - } - } - } -} diff --git a/src/libnative/io/file_unix.rs b/src/libnative/io/file_unix.rs new file mode 100644 index 0000000000000..4b6d1813ffa4a --- /dev/null +++ b/src/libnative/io/file_unix.rs @@ -0,0 +1,573 @@ +// 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 std::sync::arc::UnsafeArc; +use std::c_str::CString; +use std::io::IoError; +use std::io; +use std::libc::{c_int, c_void}; +use std::libc; +use std::mem; +use std::rt::rtio; +use std::vec; + +use io::{IoResult, retry, keep_going}; + +pub type fd_t = libc::c_int; + +struct Inner { + fd: fd_t, + close_on_drop: bool, +} + +pub struct FileDesc { + priv inner: UnsafeArc +} + +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: UnsafeArc::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]) -> Result { + let ret = retry(|| unsafe { + libc::read(self.fd(), + buf.as_mut_ptr() as *mut libc::c_void, + buf.len() as libc::size_t) as libc::c_int + }); + if ret == 0 { + Err(io::standard_error(io::EndOfFile)) + } else if ret < 0 { + Err(super::last_error()) + } else { + Ok(ret as uint) + } + } + pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> { + let ret = keep_going(buf, |buf, len| { + unsafe { + libc::write(self.fd(), buf as *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 { + // This unsafety is fine because we're just reading off the file + // descriptor, no one is modifying this. + unsafe { (*self.inner.get()).fd } + } +} + +impl io::Reader for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::IoResult { + self.inner_read(buf) + } +} + +impl io::Writer for FileDesc { + fn write(&mut self, buf: &[u8]) -> io::IoResult<()> { + self.inner_write(buf) + } +} + +impl rtio::RtioFileStream for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).map(|i| i as int) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result { + match retry(|| unsafe { + libc::pread(self.fd(), buf.as_ptr() as *libc::c_void, + buf.len() as libc::size_t, + offset as libc::off_t) as libc::c_int + }) { + -1 => Err(super::last_error()), + n => Ok(n as int) + } + } + fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { + super::mkerr_libc(retry(|| unsafe { + libc::pwrite(self.fd(), buf.as_ptr() as *libc::c_void, + buf.len() as libc::size_t, offset as libc::off_t) + } as c_int)) + } + 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 { + 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> { + super::mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) })) + } + fn datasync(&mut self) -> Result<(), IoError> { + 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 { + retry(|| unsafe { libc::fdatasync(fd) }) + } + #[cfg(not(target_os = "macos"), not(target_os = "linux"))] + fn os_datasync(fd: c_int) -> c_int { + retry(|| unsafe { libc::fsync(fd) }) + } + } + fn truncate(&mut self, offset: i64) -> Result<(), IoError> { + super::mkerr_libc(retry(|| unsafe { + libc::ftruncate(self.fd(), offset as libc::off_t) + })) + } +} + +impl rtio::RtioPipe for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + fn clone(&self) -> ~rtio::RtioPipe { + ~FileDesc { inner: self.inner.clone() } as ~rtio::RtioPipe + } +} + +impl rtio::RtioTTY for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn get_winsize(&mut self) -> Result<(int, int), IoError> { + 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 { + warn!("error {} when closing file descriptor {}", n, self.fd); + } + } + } +} + +pub struct CFile { + priv file: *libc::FILE, + priv 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: *libc::FILE) -> CFile { + CFile { + file: file, + fd: FileDesc::new(unsafe { libc::fileno(file) }, false) + } + } + + pub fn flush(&mut self) -> Result<(), IoError> { + super::mkerr_libc(retry(|| unsafe { libc::fflush(self.file) })) + } +} + +impl rtio::RtioFileStream for CFile { + fn read(&mut self, buf: &mut [u8]) -> Result { + 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(io::standard_error(io::EndOfFile)) + } else if ret < 0 { + Err(super::last_error()) + } else { + Ok(ret as int) + } + } + + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + let ret = keep_going(buf, |buf, len| { + unsafe { + libc::fwrite(buf as *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) -> Result { + self.flush().and_then(|()| self.fd.pread(buf, offset)) + } + fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { + self.flush().and_then(|()| self.fd.pwrite(buf, offset)) + } + fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result { + let whence = match style { + io::SeekSet => libc::SEEK_SET, + io::SeekEnd => libc::SEEK_END, + io::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) -> Result { + let ret = unsafe { libc::ftell(self.file) }; + if ret < 0 { + Err(super::last_error()) + } else { + Ok(ret as u64) + } + } + fn fsync(&mut self) -> Result<(), IoError> { + self.flush().and_then(|()| self.fd.fsync()) + } + fn datasync(&mut self) -> Result<(), IoError> { + self.flush().and_then(|()| self.fd.fsync()) + } + fn truncate(&mut self, offset: i64) -> Result<(), IoError> { + self.flush().and_then(|()| self.fd.truncate(offset)) + } +} + +impl Drop for CFile { + fn drop(&mut self) { + unsafe { let _ = libc::fclose(self.file); } + } +} + +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), + }; + + match retry(|| unsafe { libc::open(path.with_ref(|p| p), flags, mode) }) { + -1 => Err(super::last_error()), + fd => Ok(FileDesc::new(fd, true)), + } +} + +pub fn mkdir(p: &CString, mode: io::FilePermission) -> IoResult<()> { + super::mkerr_libc(retry(|| unsafe { + libc::mkdir(p.with_ref(|p| p), mode as libc::mode_t) + })) +} + +pub fn readdir(p: &CString) -> IoResult<~[Path]> { + use std::libc::{dirent_t}; + use std::libc::{opendir, readdir, closedir}; + + 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() + } + + extern { + fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; + } + + debug!("os::list_dir -- BEFORE OPENDIR"); + + let dir_ptr = p.with_ref(|buf| unsafe { opendir(buf) }); + + if dir_ptr as uint != 0 { + let mut paths = ~[]; + debug!("os::list_dir -- opendir() SUCCESS"); + let mut entry_ptr = unsafe { readdir(dir_ptr) }; + while entry_ptr as uint != 0 { + let cstr = unsafe { + CString::new(rust_list_dir_val(entry_ptr), false) + }; + paths.push(Path::new(cstr)); + entry_ptr = unsafe { readdir(dir_ptr) }; + } + 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(retry(|| unsafe { libc::unlink(p.with_ref(|p| p)) })) +} + +pub fn rename(old: &CString, new: &CString) -> IoResult<()> { + super::mkerr_libc(retry(|| unsafe { + libc::rename(old.with_ref(|p| p), new.with_ref(|p| p)) + })) +} + +pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> { + super::mkerr_libc(retry(|| unsafe { + libc::chmod(p.with_ref(|p| p), mode as libc::mode_t) + })) +} + +pub fn rmdir(p: &CString) -> IoResult<()> { + super::mkerr_libc(retry(|| unsafe { + libc::rmdir(p.with_ref(|p| p)) + })) +} + +pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> { + super::mkerr_libc(retry(|| unsafe { + libc::chown(p.with_ref(|p| p), uid as libc::uid_t, + gid as libc::gid_t) + })) +} + +pub fn 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; // FIXME: read PATH_MAX from C ffi? + } + let mut buf = vec::with_capacity::(len as uint); + match retry(|| 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: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_libc(retry(|| unsafe { + libc::symlink(src.with_ref(|p| p), dst.with_ref(|p| p)) + })) +} + +pub fn link(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_libc(retry(|| unsafe { + libc::link(src.with_ref(|p| p), dst.with_ref(|p| p)) + })) +} + +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 { + let mut stat: libc::stat = unsafe { mem::uninit() }; + match retry(|| 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 { + let mut stat: libc::stat = unsafe { mem::uninit() }; + match retry(|| 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<()> { + let buf = libc::utimbuf { + actime: (atime / 1000) as libc::time_t, + modtime: (mtime / 1000) as libc::time_t, + }; + super::mkerr_libc(retry(|| unsafe { + libc::utime(p.with_ref(|p| p), &buf) + })) +} + +#[cfg(test)] +mod tests { + use super::{CFile, FileDesc}; + use std::io; + use std::libc; + use std::os; + use std::rt::rtio::RtioFileStream; + + #[ignore(cfg(target_os = "freebsd"))] // 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. + unsafe { + let os::Pipe { input, out } = os::pipe(); + let mut reader = FileDesc::new(input, true); + let mut writer = FileDesc::new(out, true); + + writer.inner_write(bytes!("test")).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 => fail!("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(bytes!("test")).unwrap(); + let mut buf = [0u8, ..4]; + let _ = file.seek(0, io::SeekSet).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 => fail!("invalid read: {:?}", r) + } + } + } +} + diff --git a/src/libnative/io/file_win32.rs b/src/libnative/io/file_win32.rs new file mode 100644 index 0000000000000..9fe57536da313 --- /dev/null +++ b/src/libnative/io/file_win32.rs @@ -0,0 +1,516 @@ +// 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 win32-based file I/O + +use std::c_str::CString; +use std::cast; +use std::io::IoError; +use std::io; +use std::libc::{c_int, c_void}; +use std::libc; +use std::mem; +use std::os::win32::{as_utf16_p, fill_utf16_buf_and_decode}; +use std::ptr; +use std::rt::rtio; +use std::str; +use std::sync::arc::UnsafeArc; +use std::vec; + +use io::IoResult; + +pub type fd_t = libc::c_int; + +struct Inner { + fd: fd_t, + close_on_drop: bool, +} + +pub struct FileDesc { + priv inner: UnsafeArc +} + +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: UnsafeArc::new(Inner { + fd: fd, + close_on_drop: close_on_drop + }) } + } + + pub fn inner_read(&mut self, buf: &mut [u8]) -> Result { + let mut read = 0; + let ret = unsafe { + libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID, + buf.len() as libc::DWORD, &mut read, + ptr::mut_null()) + }; + if ret != 0 { + Ok(read as uint) + } else { + Err(super::last_error()) + } + } + pub fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> { + let mut cur = buf.as_ptr(); + let mut remaining = buf.len(); + while remaining > 0 { + let mut amt = 0; + let ret = unsafe { + libc::WriteFile(self.handle(), cur as libc::LPVOID, + remaining as libc::DWORD, &mut amt, + ptr::mut_null()) + }; + if ret != 0 { + remaining -= amt as uint; + cur = unsafe { cur.offset(amt as int) }; + } else { + return Err(super::last_error()) + } + } + Ok(()) + } + + pub fn fd(&self) -> fd_t { + // This unsafety is fine because we're just reading off the file + // descriptor, no one is modifying this. + unsafe { (*self.inner.get()).fd } + } + + pub fn handle(&self) -> libc::HANDLE { + unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE } + } +} + +impl io::Reader for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::IoResult { + self.inner_read(buf) + } +} + +impl io::Writer for FileDesc { + fn write(&mut self, buf: &[u8]) -> io::IoResult<()> { + self.inner_write(buf) + } +} + +impl rtio::RtioFileStream for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf).map(|i| i as int) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + + fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result { + let mut read = 0; + let mut overlap: libc::OVERLAPPED = unsafe { mem::init() }; + 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) -> Result<(), IoError> { + let mut cur = buf.as_ptr(); + let mut remaining = buf.len(); + let mut overlap: libc::OVERLAPPED = unsafe { mem::init() }; + 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: io::SeekStyle) -> Result { + let whence = match style { + io::SeekSet => libc::FILE_BEGIN, + io::SeekEnd => libc::FILE_END, + io::SeekCur => libc::FILE_CURRENT, + }; + unsafe { + let mut newpos = 0; + match libc::SetFilePointerEx(self.handle(), pos, &mut newpos, + whence) { + 0 => Err(super::last_error()), + _ => Ok(newpos as u64), + } + } + } + fn tell(&self) -> Result { + // This transmute is fine because our seek implementation doesn't + // actually use the mutable self at all. + unsafe { cast::transmute_mut(self).seek(0, io::SeekCur) } + } + + fn fsync(&mut self) -> Result<(), IoError> { + super::mkerr_winbool(unsafe { + libc::FlushFileBuffers(self.handle()) + }) + } + + fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); } + + fn truncate(&mut self, offset: i64) -> Result<(), IoError> { + let orig_pos = try!(self.tell()); + let _ = try!(self.seek(offset, io::SeekSet)); + let ret = unsafe { + match libc::SetEndOfFile(self.handle()) { + 0 => Err(super::last_error()), + _ => Ok(()) + } + }; + let _ = self.seek(orig_pos as i64, io::SeekSet); + return ret; + } +} + +impl rtio::RtioPipe for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + fn clone(&self) -> ~rtio::RtioPipe { + ~FileDesc { inner: self.inner.clone() } as ~rtio::RtioPipe + } +} + +impl rtio::RtioTTY for FileDesc { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner_read(buf) + } + fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.inner_write(buf) + } + fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> { + Err(super::unimpl()) + } + fn get_winsize(&mut self) -> Result<(int, int), IoError> { + 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 { + warn!("error {} when closing file descriptor {}", n, self.fd); + } + } + } +} + +pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess) + -> IoResult { + // Flags passed to open_osfhandle + let flags = match fm { + io::Open => 0, + io::Append => libc::O_APPEND, + io::Truncate => libc::O_TRUNC, + }; + let flags = match fa { + io::Read => flags | libc::O_RDONLY, + io::Write => flags | libc::O_WRONLY | libc::O_CREAT, + io::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT, + }; + + let mut dwDesiredAccess = match fa { + io::Read => libc::FILE_GENERIC_READ, + io::Write => libc::FILE_GENERIC_WRITE, + io::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 + // emulate unix semantics by enabling all sharing by allowing things such as + // deleting a file while it's still open. + let dwShareMode = libc::FILE_SHARE_READ | libc::FILE_SHARE_WRITE | + libc::FILE_SHARE_DELETE; + + let dwCreationDisposition = match (fm, fa) { + (io::Truncate, io::Read) => libc::TRUNCATE_EXISTING, + (io::Truncate, _) => libc::CREATE_ALWAYS, + (io::Open, io::Read) => libc::OPEN_EXISTING, + (io::Open, _) => libc::CREATE_NEW, + (io::Append, io::Read) => { + dwDesiredAccess |= libc::FILE_APPEND_DATA; + libc::OPEN_EXISTING + } + (io::Append, _) => { + dwDesiredAccess &= !libc::FILE_WRITE_DATA; + dwDesiredAccess |= libc::FILE_APPEND_DATA; + libc::OPEN_ALWAYS + } + }; + + let mut dwFlagsAndAttributes = libc::FILE_ATTRIBUTE_NORMAL; + // Compat with unix, this allows opening directories (see libuv) + dwFlagsAndAttributes |= libc::FILE_FLAG_BACKUP_SEMANTICS; + + let handle = as_utf16_p(path.as_str().unwrap(), |buf| unsafe { + libc::CreateFileW(buf, + dwDesiredAccess, + dwShareMode, + ptr::mut_null(), + dwCreationDisposition, + dwFlagsAndAttributes, + ptr::mut_null()) + }); + if handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE { + Err(super::last_error()) + } else { + let fd = unsafe { + libc::open_osfhandle(handle as libc::intptr_t, flags) + }; + if fd < 0 { + let _ = unsafe { libc::CloseHandle(handle) }; + Err(super::last_error()) + } else { + Ok(FileDesc::new(fd, true)) + } + } +} + +pub fn mkdir(p: &CString, _mode: io::FilePermission) -> IoResult<()> { + super::mkerr_winbool(unsafe { + // FIXME: turn mode into something useful? #2623 + as_utf16_p(p.as_str().unwrap(), |buf| { + libc::CreateDirectoryW(buf, ptr::mut_null()) + }) + }) +} + +pub fn readdir(p: &CString) -> IoResult<~[Path]> { + use rt::global_heap::malloc_raw; + + 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() + } + + #[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 = Path::new(unsafe { + CString::new(p.with_ref(|p| p), false) + }).join("*"); + as_utf16_p(star.as_str().unwrap(), |path_ptr| unsafe { + let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); + let find_handle = libc::FindFirstFileW(path_ptr, wfd_ptr as libc::HANDLE); + if find_handle as libc::c_int != libc::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 as *c_void); + 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, + libc::wcslen(fp_buf) as uint); + let fp_trimmed = str::truncate_utf16_at_nul(fp_vec); + let fp_str = str::from_utf16(fp_trimmed) + .expect("rust_list_dir_wfd_fp_buf returned invalid UTF-16"); + paths.push(Path::new(fp_str)); + } + more_files = libc::FindNextFileW(find_handle, + wfd_ptr as libc::HANDLE); + } + assert!(libc::FindClose(find_handle) != 0); + libc::free(wfd_ptr as *mut c_void); + Ok(prune(p, paths)) + } else { + Err(super::last_error()) + } + }) +} + +pub fn unlink(p: &CString) -> IoResult<()> { + super::mkerr_winbool(unsafe { + as_utf16_p(p.as_str().unwrap(), |buf| { + libc::DeleteFileW(buf) + }) + }) +} + +pub fn rename(old: &CString, new: &CString) -> IoResult<()> { + super::mkerr_winbool(unsafe { + as_utf16_p(old.as_str().unwrap(), |old| { + as_utf16_p(new.as_str().unwrap(), |new| { + libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING) + }) + }) + }) +} + +pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> { + super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe { + libc::wchmod(p, mode as libc::c_int) + })) +} + +pub fn rmdir(p: &CString) -> IoResult<()> { + super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe { + libc::wrmdir(p) + })) +} + +pub fn chown(_p: &CString, _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 { + // FIXME: I have a feeling that this reads intermediate symlinks as well. + let handle = unsafe { + 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 as int == libc::INVALID_HANDLE_VALUE as int { + return Err(super::last_error()) + } + // Specify (sz - 1) because the documentation states that it's the size + // without the null pointer + let ret = fill_utf16_buf_and_decode(|buf, sz| unsafe { + libc::GetFinalPathNameByHandleW(handle, + buf as *u16, + sz - 1, + libc::VOLUME_NAME_DOS) + }); + let ret = match ret { + 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<()> { + super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| { + as_utf16_p(dst.as_str().unwrap(), |dst| { + unsafe { libc::CreateSymbolicLinkW(dst, src, 0) } + }) as libc::BOOL + })) +} + +pub fn link(src: &CString, dst: &CString) -> IoResult<()> { + super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| { + as_utf16_p(dst.as_str().unwrap(), |dst| { + unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) } + }) + })) +} + +fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat { + let path = unsafe { CString::new(path.with_ref(|p| p), false) }; + 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, + } + } +} + +pub fn stat(p: &CString) -> IoResult { + let mut stat: libc::stat = unsafe { mem::uninit() }; + as_utf16_p(p.as_str().unwrap(), |up| { + match unsafe { libc::wstat(up, &mut stat) } { + 0 => Ok(mkstat(&stat, p)), + _ => Err(super::last_error()), + } + }) +} + +pub fn lstat(_p: &CString) -> IoResult { + // FIXME: implementation is missing + Err(super::unimpl()) +} + +pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> { + let buf = libc::utimbuf { + actime: (atime / 1000) as libc::time64_t, + modtime: (mtime / 1000) as libc::time64_t, + }; + super::mkerr_libc(as_utf16_p(p.as_str().unwrap(), |p| unsafe { + libc::wutime(p, &buf) + })) +} diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index 2f4dc7817d353..ca3a0ccc5a293 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -42,10 +42,16 @@ pub use self::process::Process; // Native I/O implementations pub mod addrinfo; -pub mod file; pub mod net; pub mod process; +#[cfg(unix)] +#[path = "file_unix.rs"] +pub mod file; +#[cfg(windows)] +#[path = "file_win32.rs"] +pub mod file; + #[cfg(target_os = "macos")] #[cfg(target_os = "freebsd")] #[cfg(target_os = "android")] @@ -97,7 +103,7 @@ fn translate_error(errno: i32, detail: bool) -> IoError { libc::WSAECONNABORTED => (io::ConnectionAborted, "connection aborted"), libc::WSAEADDRNOTAVAIL => (io::ConnectionRefused, "address not available"), libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"), - libc::ERROR_BROKEN_PIPE => (io::BrokenPipe, "the pipe has ended"), + libc::ERROR_BROKEN_PIPE => (io::EndOfFile, "the pipe has ended"), x => { debug!("ignoring {}: {}", x, os::last_os_error()); @@ -185,6 +191,24 @@ fn retry(f: || -> libc::c_int) -> libc::c_int { } } +fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 { + let origamt = data.len(); + let mut data = data.as_ptr(); + let mut amt = origamt; + while amt > 0 { + let ret = retry(|| f(data, amt) as libc::c_int); + if ret == 0 { + break + } else if ret != -1 { + amt -= ret as uint; + data = unsafe { data.offset(ret as int) }; + } else { + return ret as i64; + } + } + return (origamt - amt) as i64; +} + /// Implementation of rt::rtio's IoFactory trait to generate handles to the /// native I/O functionality. pub struct IoFactory { diff --git a/src/libnative/io/net.rs b/src/libnative/io/net.rs index d71f7544225c6..7445e4c099261 100644 --- a/src/libnative/io/net.rs +++ b/src/libnative/io/net.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[allow(non_camel_case_types)]; - use std::cast; use std::io::net::ip; use std::io; @@ -18,8 +16,7 @@ use std::mem; use std::rt::rtio; use std::sync::arc::UnsafeArc; -use super::{IoResult, retry}; -use super::file::keep_going; +use super::{IoResult, retry, keep_going}; //////////////////////////////////////////////////////////////////////////////// // sockaddr and misc bindings @@ -323,16 +320,14 @@ impl rtio::RtioTcpStream for TcpStream { } } fn write(&mut self, buf: &[u8]) -> IoResult<()> { - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::send(self.fd(), - buf as *mut libc::c_void, - len as wrlen, - 0) as i64 - } + let ret = keep_going(buf, |buf, len| unsafe { + libc::send(self.fd(), + buf as *mut libc::c_void, + len as wrlen, + 0) as i64 }); if ret < 0 { - Err(last_error()) + Err(super::last_error()) } else { Ok(()) } diff --git a/src/libnative/io/pipe_unix.rs b/src/libnative/io/pipe_unix.rs index 784d8650689a4..9e81dc02cc596 100644 --- a/src/libnative/io/pipe_unix.rs +++ b/src/libnative/io/pipe_unix.rs @@ -17,8 +17,8 @@ use std::rt::rtio; use std::sync::arc::UnsafeArc; use std::intrinsics; -use super::{IoResult, retry}; -use super::file::{keep_going, fd_t}; +use super::{IoResult, retry, keep_going}; +use super::file::fd_t; fn unix_socket(ty: libc::c_int) -> IoResult { match unsafe { libc::socket(libc::AF_UNIX, ty, 0) } { diff --git a/src/libnative/io/timer_helper.rs b/src/libnative/io/timer_helper.rs index 0f3ed1482294b..7669d4a658fad 100644 --- a/src/libnative/io/timer_helper.rs +++ b/src/libnative/io/timer_helper.rs @@ -20,8 +20,6 @@ //! can be created in the future and there must be no active timers at that //! time. -#[allow(non_camel_case_types)]; - use std::cast; use std::rt; use std::unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; @@ -100,7 +98,6 @@ mod imp { use io::file::FileDesc; - #[allow(non_camel_case_types)] pub type signal = libc::c_int; pub fn new() -> (signal, signal) { diff --git a/src/libnative/io/timer_other.rs b/src/libnative/io/timer_other.rs index 9f332adb27b05..0784b5ee04845 100644 --- a/src/libnative/io/timer_other.rs +++ b/src/libnative/io/timer_other.rs @@ -46,8 +46,6 @@ //! //! Note that all time units in this file are in *milliseconds*. -#[allow(non_camel_case_types)]; - use std::comm::Data; use std::libc; use std::mem; diff --git a/src/libnative/io/timer_timerfd.rs b/src/libnative/io/timer_timerfd.rs index baafe3f4850c1..7feeaa4768c9a 100644 --- a/src/libnative/io/timer_timerfd.rs +++ b/src/libnative/io/timer_timerfd.rs @@ -28,8 +28,6 @@ //! //! As with timer_other, all units in this file are in units of millseconds. -#[allow(non_camel_case_types)]; - use std::comm::Data; use std::libc; use std::ptr; diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index c28a1175494f9..238e4c23b452e 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -49,6 +49,7 @@ html_favicon_url = "http://www.rust-lang.org/favicon.ico", html_root_url = "http://static.rust-lang.org/doc/master")]; #[deny(unused_result, unused_must_use)]; +#[allow(non_camel_case_types)]; // NB this crate explicitly does *not* allow glob imports, please seriously // consider whether they're needed before adding that feature here (the diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs index 73bf4a1e69a88..6b692112f5ff5 100644 --- a/src/libstd/libc.rs +++ b/src/libstd/libc.rs @@ -1745,6 +1745,10 @@ pub mod consts { pub static OPEN_EXISTING: DWORD = 3; pub static TRUNCATE_EXISTING: DWORD = 5; + pub static FILE_APPEND_DATA: DWORD = 0x00000004; + pub static FILE_READ_DATA: DWORD = 0x00000001; + pub static FILE_WRITE_DATA: DWORD = 0x00000002; + pub static FILE_ATTRIBUTE_ARCHIVE: DWORD = 0x20; pub static FILE_ATTRIBUTE_COMPRESSED: DWORD = 0x800; pub static FILE_ATTRIBUTE_DEVICE: DWORD = 0x40; @@ -1791,6 +1795,18 @@ pub mod consts { pub static FILE_WRITE_ATTRIBUTES: DWORD = 0x00000100; pub static FILE_READ_ATTRIBUTES: DWORD = 0x00000080; + pub static STANDARD_RIGHTS_READ: DWORD = 0x20000; + pub static STANDARD_RIGHTS_WRITE: DWORD = 0x20000; + pub static FILE_WRITE_EA: DWORD = 0x00000010; + pub static FILE_READ_EA: DWORD = 0x00000008; + pub static FILE_GENERIC_READ: DWORD = + STANDARD_RIGHTS_READ | FILE_READ_DATA | + FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE; + pub static FILE_GENERIC_WRITE: DWORD = + STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | + FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | + SYNCHRONIZE; + pub static FILE_BEGIN: DWORD = 0; pub static FILE_CURRENT: DWORD = 1; pub static FILE_END: DWORD = 2; @@ -4231,6 +4247,7 @@ pub mod funcs { pub mod msvcrt { use libc::types::os::arch::c95::{c_int, c_long}; + use libc::types::os::arch::c99::intptr_t; #[nolink] extern { @@ -4239,6 +4256,10 @@ pub mod funcs { #[link_name = "_get_osfhandle"] pub fn get_osfhandle(fd: c_int) -> c_long; + + #[link_name = "_open_osfhandle"] + pub fn open_osfhandle(osfhandle: intptr_t, + flags: c_int) -> c_int; } } } From 40ab198356bbec05b308b2e61ebc6fe9e23ade9d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 26 Feb 2014 13:03:40 -0800 Subject: [PATCH 3/4] rustc: Use libnative for the compiler The compiler itself doesn't necessarily need any features of green threading such as spawning tasks and lots of I/O, so libnative is slightly more appropriate for rustc to use itself. This should also help the rusti bot which is currently incompatible with libuv. --- mk/crates.mk | 4 ++-- src/driver/driver.rs | 7 ++++++- src/libnative/io/file_win32.rs | 1 + src/libstd/io/fs.rs | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/mk/crates.mk b/mk/crates.mk index 09b1dd699105b..45b6ed1a058d0 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -81,8 +81,8 @@ DEPS_test := std extra collections getopts serialize term DEPS_time := std serialize TOOL_DEPS_compiletest := test green rustuv getopts -TOOL_DEPS_rustdoc := rustdoc green rustuv -TOOL_DEPS_rustc := rustc green rustuv +TOOL_DEPS_rustdoc := rustdoc native +TOOL_DEPS_rustc := rustc native TOOL_SOURCE_compiletest := $(S)src/compiletest/compiletest.rs TOOL_SOURCE_rustdoc := $(S)src/driver/driver.rs TOOL_SOURCE_rustc := $(S)src/driver/driver.rs diff --git a/src/driver/driver.rs b/src/driver/driver.rs index ca462fc7a3922..0ceb12064b0eb 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs @@ -8,10 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[no_uv]; + #[cfg(rustdoc)] extern crate this = "rustdoc"; #[cfg(rustc)] extern crate this = "rustc"; -fn main() { this::main() } +extern crate native; + +#[start] +fn start(argc: int, argv: **u8) -> int { native::start(argc, argv, this::main) } diff --git a/src/libnative/io/file_win32.rs b/src/libnative/io/file_win32.rs index 9fe57536da313..e880bd05cf7fc 100644 --- a/src/libnative/io/file_win32.rs +++ b/src/libnative/io/file_win32.rs @@ -433,6 +433,7 @@ pub fn readlink(p: &CString) -> IoResult { libc::VOLUME_NAME_DOS) }); let ret = match ret { + Some(ref s) if s.starts_with(r"\\?\") => Ok(Path::new(s.slice_from(4))), Some(s) => Ok(Path::new(s)), None => Err(super::last_error()), }; diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index d72abb272c28d..5f070ef782b4e 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -1217,6 +1217,6 @@ mod test { check!(File::create(&tmpdir.join("test")).write(bytes)); let actual = check!(File::open(&tmpdir.join("test")).read_to_end()); - assert!(actual.as_slice == bytes); + assert!(actual.as_slice() == bytes); }) } From 8c157ed63d95b4b4ff122493228eb0b5acf1e2ae Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 26 Feb 2014 13:06:45 -0800 Subject: [PATCH 4/4] native: Recognize EISDIR This recognizes the EISDIR error code on both windows and unix platforms to provide a more descriptive error condition. --- src/libnative/io/mod.rs | 8 ++++++++ src/libstd/libc.rs | 1 + 2 files changed, 9 insertions(+) diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs index ca3a0ccc5a293..2e3e9b3b50637 100644 --- a/src/libnative/io/mod.rs +++ b/src/libnative/io/mod.rs @@ -105,6 +105,13 @@ fn translate_error(errno: i32, detail: bool) -> IoError { libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"), libc::ERROR_BROKEN_PIPE => (io::EndOfFile, "the pipe has ended"), + // libuv maps this error code to EISDIR. we do too. if it is found + // to be incorrect, we can add in some more machinery to only + // return this message when ERROR_INVALID_FUNCTION after certain + // win32 calls. + libc::ERROR_INVALID_FUNCTION => (io::InvalidInput, + "illegal operation on a directory"), + x => { debug!("ignoring {}: {}", x, os::last_os_error()); (io::OtherIoError, "unknown error") @@ -127,6 +134,7 @@ fn translate_error(errno: i32, detail: bool) -> IoError { libc::EADDRNOTAVAIL => (io::ConnectionRefused, "address not available"), libc::EADDRINUSE => (io::ConnectionRefused, "address in use"), libc::ENOENT => (io::FileNotFound, "no such file or directory"), + libc::EISDIR => (io::InvalidInput, "illegal operation on a directory"), // These two constants can have the same value on some systems, but // different values on others, so we can't use a match clause diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs index 6b692112f5ff5..07be753925f20 100644 --- a/src/libstd/libc.rs +++ b/src/libstd/libc.rs @@ -1623,6 +1623,7 @@ pub mod consts { pub static O_NOINHERIT: c_int = 128; pub static ERROR_SUCCESS : c_int = 0; + pub static ERROR_INVALID_FUNCTION: c_int = 1; pub static ERROR_FILE_NOT_FOUND: c_int = 2; pub static ERROR_ACCESS_DENIED: c_int = 5; pub static ERROR_INVALID_HANDLE : c_int = 6;