diff options
| author | Jeff Olson <olson.jeffery@gmail.com> | 2013-08-26 07:24:10 -0700 |
|---|---|---|
| committer | Jeff Olson <olson.jeffery@gmail.com> | 2013-09-16 23:17:46 -0700 |
| commit | af650572e0fceb94b387db50ee31f19ee000fe4b (patch) | |
| tree | e50fea65e834311dcd3a1ee9728bb68b33f42532 /src/libstd | |
| parent | b75e07501b8a68bc2acf61d506d5b21498448c0c (diff) | |
| download | rust-af650572e0fceb94b387db50ee31f19ee000fe4b.tar.gz rust-af650572e0fceb94b387db50ee31f19ee000fe4b.zip | |
std/rt: in-progress file io work
std: remove unneeded field from RequestData struct std: rt::uv::file - map us_fs_stat & start refactoring calls into FsRequest std: stubbing out stat calls from the top-down into uvio std: us_fs_* operations are now by-val self methods on FsRequest std: post-rebase cleanup std: add uv_fs_mkdir|rmdir + tests & minor test cleanup in rt::uv::file WORKING: fleshing out FileStat and FileInfo + tests std: reverting test files.. refactoring back and cleanup...
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/rt/io/file.rs | 162 | ||||
| -rw-r--r-- | src/libstd/rt/io/mod.rs | 24 | ||||
| -rw-r--r-- | src/libstd/rt/rtio.rs | 9 | ||||
| -rw-r--r-- | src/libstd/rt/uv/file.rs | 510 | ||||
| -rw-r--r-- | src/libstd/rt/uv/uvio.rs | 72 | ||||
| -rw-r--r-- | src/libstd/rt/uv/uvll.rs | 87 |
6 files changed, 661 insertions, 203 deletions
diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index c24f4eb257e..ee102c3a97f 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -11,12 +11,15 @@ use prelude::*; use super::support::PathLike; use super::{Reader, Writer, Seek}; -use super::{SeekSet, SeekCur, SeekEnd, SeekStyle}; +use super::{SeekStyle,SeekSet, SeekCur, SeekEnd, + Open, Read, Create, ReadWrite}; use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; use rt::io::{io_error, read_error, EndOfFile, - FileMode, FileAccess, Open, Read, Create, ReadWrite}; + FileMode, FileAccess, FileStat}; use rt::local::Local; -use rt::test::*; +use option::{Some, None}; +use path::Path; +use super::super::test::*; /// Open a file for reading/writing, as indicated by `path`. pub fn open<P: PathLike>(path: &P, @@ -145,6 +148,123 @@ impl Seek for FileStream { } } +pub struct FileInfo(Path); + +/// FIXME: DOCS +impl<'self> FileInfo { + pub fn new<P: PathLike>(path: &P) -> FileInfo { + do path.path_as_str |p| { + FileInfo(Path(p)) + } + } + // FIXME #8873 can't put this in FileSystemInfo + pub fn get_path(&'self self) -> &'self Path { + &(**self) + } + pub fn stat(&self) -> Option<FileStat> { + do io_error::cond.trap(|_| { + // FIXME: can we do something more useful here? + }).inside { + stat(self.get_path()) + } + } + pub fn exists(&self) -> bool { + match self.stat() { + Some(s) => { + match s.is_file { + true => { + true + }, + false => { + // FIXME: raise condition? + false + } + } + }, + None => false + } + } + pub fn is_file(&self) -> bool { + match self.stat() { + Some(s) => s.is_file, + None => { + // FIXME: raise condition + false + } + } + } + pub fn open(&self, mode: FileMode, access: FileAccess) -> Option<FileStream> { + match self.is_file() { + true => { + open(self.get_path(), mode, access) + }, + false => { + // FIXME: raise condition + None + } + } + } + //fn open_read(&self) -> FileStream; + //fn open_write(&self) -> FileStream; + //fn create(&self) -> FileStream; + //fn truncate(&self) -> FileStream; + //fn open_or_create(&self) -> FileStream; + //fn create_or_truncate(&self) -> FileStream; + //fn unlink(&self); +} + +/* +/// FIXME: DOCS +impl DirectoryInfo<'self> { + fn new<P: PathLike>(path: &P) -> FileInfo { + FileInfo(Path(path.path_as_str())) + } + // FIXME #8873 can't put this in FileSystemInfo + fn get_path(&'self self) -> &'self Path { + &*self + } + fn stat(&self) -> Option<FileStat> { + file::stat(self.get_path()) + } + fn exists(&self) -> bool { + do io_error::cond.trap(|_| { + }).inside { + match self.stat() { + Some(_) => true, + None => false + } + } + } + fn is_dir(&self) -> bool { + + } + fn create(&self); + fn get_subdirs(&self, filter: &str) -> ~[Path]; + fn get_files(&self, filter: &str) -> ~[Path]; +} +*/ + +/// Given a `rt::io::support::PathLike`, query the file system to get +/// information about a file, directory, etc. +/// +/// Returns a `Some(PathInfo)` on success, and raises a `rt::io::IoError` condition +/// on failure and returns `None`. +pub fn stat<P: PathLike>(path: &P) -> Option<FileStat> { + let open_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_stat(path) + }; + match open_result { + Ok(p) => { + Some(p) + }, + Err(ioerr) => { + read_error::cond.raise(ioerr); + None + } + } +} + fn file_test_smoke_test_impl() { do run_in_mt_newsched_task { let message = "it's alright. have a good time"; @@ -273,7 +393,6 @@ fn file_test_io_seek_and_tell_smoke_test() { } fn file_test_io_seek_and_write_impl() { - use io; do run_in_mt_newsched_task { use str; let initial_msg = "food-is-yummy"; @@ -293,8 +412,7 @@ fn file_test_io_seek_and_write_impl() { read_stream.read(read_mem); } unlink(filename); - let read_str = str::from_utf8(read_mem); - io::println(fmt!("read_str: '%?' final_msg: '%?'", read_str, final_msg)); + let read_str = str::from_bytes(read_mem); assert!(read_str == final_msg.to_owned()); } } @@ -343,3 +461,35 @@ fn file_test_io_seek_shakedown_impl() { fn file_test_io_seek_shakedown() { file_test_io_seek_shakedown_impl(); } + +#[test] +fn file_test_stat_is_correct_on_is_file() { + do run_in_newsched_task { + let filename = &Path("./tmp/file_stat_correct_on_is_file.txt"); + { + let mut fs = open(filename, Create, ReadWrite).unwrap(); + let msg = "hw"; + fs.write(msg.as_bytes()); + } + let stat_res = match stat(filename) { + Some(s) => s, + None => fail!("shouldn't happen") + }; + assert!(stat_res.is_file); + } +} + +#[test] +fn file_test_stat_is_correct_on_is_dir() { + //assert!(false); +} + +#[test] +fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { + //assert!(false); +} + +#[test] +fn file_test_fileinfo_check_exists_before_and_after_file_creation() { + //assert!(false); +} diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index 59ca5d57759..55c7cfd8285 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -245,6 +245,7 @@ Out of scope use prelude::*; use to_str::ToStr; use str::{StrSlice, OwnedStr}; +use path::Path; // Reexports pub use self::stdio::stdin; @@ -596,3 +597,26 @@ pub enum FileAccess { Write, ReadWrite } + +pub struct FileStat { + /// A `Path` object containing information about the `PathInfo`'s location + path: Path, + /// `true` if the file pointed at by the `PathInfo` is a regular file + is_file: bool, + /// `true` if the file pointed at by the `PathInfo` is a directory + is_dir: bool + // `true` if the file pointed at by the `PathInfo` is a link (what this means + // is platform dependant) + /* + /// The file pointed at by the `PathInfo`'s size in bytes + size: u64, + /// The file pointed at by the `PathInfo`'s time date in platform-dependent msecs + created: u64, + /// The file pointed at by the `PathInfo`'s last-modification time in + /// platform-dependent msecs + modified: u64, + /// The file pointed at by the `PathInfo`'s last-accessd time (e.g. read) in + /// platform-dependent msecs + accessed: u64, + */ +} diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index c9c402baaf0..f08949e9165 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -18,7 +18,7 @@ use rt::uv::uvio; use path::Path; use super::io::support::PathLike; use super::io::{SeekStyle}; -use super::io::{FileMode, FileAccess}; +use super::io::{FileMode, FileAccess, FileStat}; // XXX: ~object doesn't work currently so these are some placeholder // types to use instead @@ -74,6 +74,13 @@ pub trait IoFactory { -> Result<~RtioFileStream, IoError>; fn fs_unlink<P: PathLike>(&mut self, path: &P) -> Result<(), IoError>; fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError>; + fn fs_stat<P: PathLike>(&mut self, path: &P) -> Result<FileStat, IoError>; + //fn fs_fstat(&mut self, fd: c_int) -> Result<FileStat, IoError>; +} + +pub trait RtioStream { + fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError>; + fn write(&mut self, buf: &[u8]) -> Result<(), IoError>; } pub trait RtioTcpListener : RtioSocket { diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index e87e2d4b1e4..32e248254f4 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -24,12 +24,11 @@ pub struct FsRequest(*uvll::uv_fs_t); impl Request for FsRequest; pub struct RequestData { - complete_cb: Option<FsCallback>, - raw_fd: Option<c_int> + complete_cb: Option<FsCallback> } impl FsRequest { - pub fn new(cb: Option<FsCallback>) -> FsRequest { + pub fn new_REFACTOR_ME(cb: Option<FsCallback>) -> FsRequest { let fs_req = unsafe { malloc_req(UV_FS) }; assert!(fs_req.is_not_null()); let fs_req: FsRequest = NativeHandle::from_native_handle(fs_req); @@ -37,64 +36,174 @@ impl FsRequest { fs_req } - fn open_common<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int, - cb: Option<FsCallback>) -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let req = FsRequest::new(cb); - let result = path.path_as_str(|p| { + pub fn new() -> FsRequest { + let fs_req = unsafe { malloc_req(UV_FS) }; + assert!(fs_req.is_not_null()); + let fs_req: FsRequest = NativeHandle::from_native_handle(fs_req); + fs_req + } + + pub fn open<P: PathLike>(self, loop_: &Loop, path: &P, flags: int, mode: int, + cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_open(loop_.native_handle(), - req.native_handle(), p, flags, mode, complete_cb_ptr) as int + self.native_handle(), p, flags, mode, complete_cb_ptr) }) }); - if is_sync { req.cleanup_and_delete(); } - result } - pub fn open<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int, - cb: FsCallback) { - FsRequest::open_common(loop_, path, flags, mode, Some(cb)); + + pub fn open_sync<P: PathLike>(self, loop_: &Loop, path: &P, + flags: int, mode: int) -> Result<c_int, UvError> { + let complete_cb_ptr = self.req_boilerplate(None); + let result = path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_open(loop_.native_handle(), + self.native_handle(), p, flags, mode, complete_cb_ptr) + }) + }); + self.sync_cleanup(result) } - pub fn open_sync<P: PathLike>(loop_: &Loop, path: &P, flags: int, mode: int) - -> Result<int, UvError> { - let result = FsRequest::open_common(loop_, path, flags, mode, None); - sync_cleanup(result) + pub fn unlink<P: PathLike>(self, loop_: &Loop, path: &P, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_unlink(loop_.native_handle(), + self.native_handle(), p, complete_cb_ptr) + }) + }); } - fn unlink_common<P: PathLike>(loop_: &Loop, path: &P, cb: Option<FsCallback>) -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let req = FsRequest::new(cb); + pub fn unlink_sync<P: PathLike>(self, loop_: &Loop, path: &P) + -> Result<c_int, UvError> { + let complete_cb_ptr = self.req_boilerplate(None); let result = path.path_as_str(|p| { p.to_c_str().with_ref(|p| unsafe { uvll::fs_unlink(loop_.native_handle(), - req.native_handle(), p, complete_cb_ptr) as int + self.native_handle(), p, complete_cb_ptr) }) }); - if is_sync { req.cleanup_and_delete(); } - result + self.sync_cleanup(result) + } + + pub fn stat<P: PathLike>(self, loop_: &Loop, path: &P, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_stat(loop_.native_handle(), + self.native_handle(), p, complete_cb_ptr) + }) + }); + } + + pub fn write(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let base_ptr = buf.base as *c_void; + let len = buf.len as uint; + unsafe { + uvll::fs_write(loop_.native_handle(), self.native_handle(), + fd, base_ptr, + len, offset, complete_cb_ptr) + }; + } + pub fn write_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64) + -> Result<c_int, UvError> { + let complete_cb_ptr = self.req_boilerplate(None); + let base_ptr = buf.base as *c_void; + let len = buf.len as uint; + let result = unsafe { + uvll::fs_write(loop_.native_handle(), self.native_handle(), + fd, base_ptr, + len, offset, complete_cb_ptr) + }; + self.sync_cleanup(result) + } + + pub fn read(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + let buf_ptr = buf.base as *c_void; + let len = buf.len as uint; + unsafe { + uvll::fs_read(loop_.native_handle(), self.native_handle(), + fd, buf_ptr, + len, offset, complete_cb_ptr) + }; + } + pub fn read_sync(self, loop_: &Loop, fd: c_int, buf: Buf, offset: i64) + -> Result<c_int, UvError> { + let complete_cb_ptr = self.req_boilerplate(None); + let buf_ptr = buf.base as *c_void; + let len = buf.len as uint; + let result = unsafe { + uvll::fs_read(loop_.native_handle(), self.native_handle(), + fd, buf_ptr, + len, offset, complete_cb_ptr) + }; + self.sync_cleanup(result) + } + + pub fn close(self, loop_: &Loop, fd: c_int, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + unsafe { + uvll::fs_close(loop_.native_handle(), self.native_handle(), + fd, complete_cb_ptr) + }; + } + pub fn close_sync(self, loop_: &Loop, fd: c_int) -> Result<c_int, UvError> { + let complete_cb_ptr = self.req_boilerplate(None); + let result = unsafe { + uvll::fs_close(loop_.native_handle(), self.native_handle(), + fd, complete_cb_ptr) + }; + self.sync_cleanup(result) } - pub fn unlink<P: PathLike>(loop_: &Loop, path: &P, cb: FsCallback) { - let result = FsRequest::unlink_common(loop_, path, Some(cb)); - sync_cleanup(result); + + pub fn mkdir<P: PathLike>(self, loop_: &Loop, path: &P, mode: int, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_mkdir(loop_.native_handle(), + self.native_handle(), p, mode, complete_cb_ptr) + }) + }); } - pub fn unlink_sync<P: PathLike>(loop_: &Loop, path: &P) -> Result<int, UvError> { - let result = FsRequest::unlink_common(loop_, path, None); - sync_cleanup(result) + + pub fn rmdir<P: PathLike>(self, loop_: &Loop, path: &P, cb: FsCallback) { + let complete_cb_ptr = self.req_boilerplate(Some(cb)); + path.path_as_str(|p| { + p.to_c_str().with_ref(|p| unsafe { + uvll::fs_rmdir(loop_.native_handle(), + self.native_handle(), p, complete_cb_ptr) + }) + }); } + // accessors/utility funcs + fn sync_cleanup(self, result: c_int) + -> Result<c_int, UvError> { + self.cleanup_and_delete(); + match status_to_maybe_uv_error(result as i32) { + Some(err) => Err(err), + None => Ok(result) + } + } + fn req_boilerplate(&self, cb: Option<FsCallback>) -> *u8 { + // XXX: this is unsafe/mutable + let result = match cb { + Some(_) => { + compl_cb as *u8 + }, + None => 0 as *u8 + }; + self.install_req_data(cb); + result + } pub fn install_req_data(&self, cb: Option<FsCallback>) { let fs_req = (self.native_handle()) as *uvll::uv_write_t; let data = ~RequestData { - complete_cb: cb, - raw_fd: None + complete_cb: cb }; unsafe { let data = transmute::<~RequestData, *c_void>(data); @@ -106,7 +215,7 @@ impl FsRequest { unsafe { let data = uvll::get_data_for_req((self.native_handle())); let data = transmute::<&*c_void, &mut ~RequestData>(&data); - return &mut **data; + &mut **data } } @@ -120,6 +229,12 @@ impl FsRequest { unsafe { Loop{handle:uvll::get_loop_from_fs_req(self.native_handle())} } } + pub fn get_stat(&self) -> uv_stat_t { + let stat = uv_stat_t::new(); + unsafe { uvll::populate_stat(self.native_handle(), &stat); } + stat + } + fn cleanup_and_delete(self) { unsafe { let data = uvll::get_data_for_req(self.native_handle()); @@ -145,100 +260,7 @@ fn sync_cleanup(result: int) match status_to_maybe_uv_error(result as i32) { Some(err) => Err(err), None => Ok(result) - } -} - -pub struct FileDescriptor(c_int); - -impl FileDescriptor { - fn new(fd: c_int) -> FileDescriptor { - FileDescriptor(fd) - } - - - pub fn from_open_req(req: &mut FsRequest) -> FileDescriptor { - FileDescriptor::new(req.get_result()) - } - - // as per bnoordhuis in #libuv: offset >= 0 uses prwrite instead of write - fn write_common(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: Option<FsCallback>) - -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let mut req = FsRequest::new(cb); - let base_ptr = buf.base as *c_void; - let len = buf.len as uint; - req.get_req_data().raw_fd = Some(self.native_handle()); - let result = unsafe { - uvll::fs_write(loop_.native_handle(), req.native_handle(), - self.native_handle(), base_ptr, - len, offset, complete_cb_ptr) as int - }; - if is_sync { req.cleanup_and_delete(); } - result - } - pub fn write(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) { - self.write_common(loop_, buf, offset, Some(cb)); - } - pub fn write_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64) - -> Result<int, UvError> { - let result = self.write_common(loop_, buf, offset, None); - sync_cleanup(result) - } - - fn read_common(&mut self, loop_: &Loop, buf: Buf, - offset: i64, cb: Option<FsCallback>) - -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let mut req = FsRequest::new(cb); - req.get_req_data().raw_fd = Some(self.native_handle()); - let buf_ptr = buf.base as *c_void; - let result = unsafe { - uvll::fs_read(loop_.native_handle(), req.native_handle(), - self.native_handle(), buf_ptr, - buf.len as uint, offset, complete_cb_ptr) as int - }; - if is_sync { req.cleanup_and_delete(); } - result - } - pub fn read(&mut self, loop_: &Loop, buf: Buf, offset: i64, cb: FsCallback) { - self.read_common(loop_, buf, offset, Some(cb)); - } - pub fn read_sync(&mut self, loop_: &Loop, buf: Buf, offset: i64) - -> Result<int, UvError> { - let result = self.read_common(loop_, buf, offset, None); - sync_cleanup(result) - } - fn close_common(self, loop_: &Loop, cb: Option<FsCallback>) -> int { - let complete_cb_ptr = match cb { - Some(_) => compl_cb as *u8, - None => 0 as *u8 - }; - let is_sync = cb.is_none(); - let req = FsRequest::new(cb); - let result = unsafe { - uvll::fs_close(loop_.native_handle(), req.native_handle(), - self.native_handle(), complete_cb_ptr) as int - }; - if is_sync { req.cleanup_and_delete(); } - result - } - pub fn close(self, loop_: &Loop, cb: FsCallback) { - self.close_common(loop_, Some(cb)); - } - pub fn close_sync(self, loop_: &Loop) -> Result<int, UvError> { - let result = self.close_common(loop_, None); - sync_cleanup(result) - } -} extern fn compl_cb(req: *uv_fs_t) { let mut req: FsRequest = NativeHandle::from_native_handle(req); // pull the user cb out of the req data @@ -261,15 +283,7 @@ extern fn compl_cb(req: *uv_fs_t) { req.cleanup_and_delete(); } -impl NativeHandle<c_int> for FileDescriptor { - fn from_native_handle(handle: c_int) -> FileDescriptor { - FileDescriptor(handle) - } - fn native_handle(&self) -> c_int { - match self { &FileDescriptor(ptr) => ptr } - } -} - +#[cfg(test)] mod test { use super::*; //use rt::test::*; @@ -279,11 +293,12 @@ mod test { use unstable::run_in_bare_thread; use path::Path; use rt::uv::{Loop, Buf, slice_to_uv_buf}; - use libc::{O_CREAT, O_RDWR, O_RDONLY, - S_IWUSR, S_IRUSR}; //NOTE: need defs for S_**GRP|S_**OTH in libc:: ... - //S_IRGRP, S_IROTH}; + use libc::{c_int, O_CREAT, O_RDWR, O_RDONLY, + S_IWUSR, S_IRUSR}; - fn file_test_full_simple_impl() { + #[test] + #[ignore(cfg(windows))] // FIXME #8814 + fn file_test_full_simple() { do run_in_bare_thread { let mut loop_ = Loop::new(); let create_flags = O_RDWR | O_CREAT; @@ -302,25 +317,27 @@ mod test { let read_buf = slice_to_uv_buf(read_mem); let read_buf_ptr: *Buf = &read_buf; let p = Path(path_str); - do FsRequest::open(&loop_, &p, create_flags as int, mode as int) + let open_req = FsRequest::new(); + do open_req.open(&loop_, &p, create_flags as int, mode as int) |req, uverr| { assert!(uverr.is_none()); - let mut fd = FileDescriptor::from_open_req(req); - let raw_fd = fd.native_handle(); + let fd = req.get_result(); let buf = unsafe { *write_buf_ptr }; - do fd.write(&req.get_loop(), buf, -1) |req, uverr| { - let fd = FileDescriptor(raw_fd); - do fd.close(&req.get_loop()) |req, _| { - let loop_ = req.get_loop(); + let write_req = FsRequest::new(); + do write_req.write(&req.get_loop(), fd, buf, -1) |req, uverr| { + let close_req = FsRequest::new(); + do close_req.close(&req.get_loop(), fd) |req, _| { assert!(uverr.is_none()); - do FsRequest::open(&loop_, &Path(path_str), read_flags as int,0) + let loop_ = req.get_loop(); + let open_req = FsRequest::new(); + do open_req.open(&loop_, &Path(path_str), read_flags as int,0) |req, uverr| { assert!(uverr.is_none()); let loop_ = req.get_loop(); - let mut fd = FileDescriptor::from_open_req(req); - let raw_fd = fd.native_handle(); + let fd = req.get_result(); let read_buf = unsafe { *read_buf_ptr }; - do fd.read(&loop_, read_buf, 0) |req, uverr| { + let read_req = FsRequest::new(); + do read_req.read(&loop_, fd, read_buf, 0) |req, uverr| { assert!(uverr.is_none()); let loop_ = req.get_loop(); // we know nread >=0 because uverr is none.. @@ -334,15 +351,17 @@ mod test { read_buf.base, nread)) }; assert!(read_str == ~"hello"); - do FileDescriptor(raw_fd).close(&loop_) |req,uverr| { + let close_req = FsRequest::new(); + do close_req.close(&loop_, fd) |req,uverr| { assert!(uverr.is_none()); let loop_ = &req.get_loop(); - do FsRequest::unlink(loop_, &Path(path_str)) + let unlink_req = FsRequest::new(); + do unlink_req.unlink(loop_, &Path(path_str)) |_,uverr| { assert!(uverr.is_none()); }; }; - } + }; }; }; }; @@ -352,7 +371,10 @@ mod test { loop_.close(); } } - fn file_test_full_simple_impl_sync() { + + #[test] + #[ignore(cfg(windows))] // FIXME #8814 + fn file_test_full_simple_sync() { do run_in_bare_thread { // setup let mut loop_ = Loop::new(); @@ -368,26 +390,31 @@ mod test { let write_val = "hello".as_bytes().to_owned(); let write_buf = slice_to_uv_buf(write_val); // open/create - let result = FsRequest::open_sync(&loop_, &Path(path_str), + let open_req = FsRequest::new(); + let result = open_req.open_sync(&loop_, &Path(path_str), create_flags as int, mode as int); assert!(result.is_ok()); - let mut fd = FileDescriptor(result.unwrap() as i32); + let fd = result.unwrap(); // write - let result = fd.write_sync(&loop_, write_buf, -1); + let write_req = FsRequest::new(); + let result = write_req.write_sync(&loop_, fd, write_buf, -1); assert!(result.is_ok()); // close - let result = fd.close_sync(&loop_); + let close_req = FsRequest::new(); + let result = close_req.close_sync(&loop_, fd); assert!(result.is_ok()); // re-open - let result = FsRequest::open_sync(&loop_, &Path(path_str), + let open_req = FsRequest::new(); + let result = open_req.open_sync(&loop_, &Path(path_str), read_flags as int,0); assert!(result.is_ok()); let len = 1028; - let mut fd = FileDescriptor(result.unwrap() as i32); + let fd = result.unwrap(); // read let read_mem: ~[u8] = vec::from_elem(len, 0u8); let buf = slice_to_uv_buf(read_mem); - let result = fd.read_sync(&loop_, buf, 0); + let read_req = FsRequest::new(); + let result = read_req.read_sync(&loop_, fd, buf, 0); assert!(result.is_ok()); let nread = result.unwrap(); // nread == 0 would be EOF.. we know it's >= zero because otherwise @@ -397,31 +424,23 @@ mod test { read_mem.slice(0, nread as uint)); assert!(read_str == ~"hello"); // close - let result = fd.close_sync(&loop_); + let close_req = FsRequest::new(); + let result = close_req.close_sync(&loop_, fd); assert!(result.is_ok()); // unlink - let result = FsRequest::unlink_sync(&loop_, &Path(path_str)); + let unlink_req = FsRequest::new(); + let result = unlink_req.unlink_sync(&loop_, &Path(path_str)); assert!(result.is_ok()); } else { fail!("nread was 0.. wudn't expectin' that."); } loop_.close(); } } - #[test] - fn file_test_full_simple() { - file_test_full_simple_impl(); - } - - #[test] - fn file_test_full_simple_sync() { - file_test_full_simple_impl_sync(); - } - fn naive_print(loop_: &Loop, input: &str) { - let mut stdout = FileDescriptor(STDOUT_FILENO); let write_val = input.as_bytes(); let write_buf = slice_to_uv_buf(write_val); - stdout.write_sync(loop_, write_buf, -1); + let write_req = FsRequest::new(); + write_req.write_sync(loop_, stdout, write_buf, -1); } #[test] @@ -433,4 +452,129 @@ mod test { loop_.close(); }; } + #[test] + fn file_test_stat_simple() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/file_test_stat_simple.txt"; + let create_flags = O_RDWR | + O_CREAT; + let mode = S_IWUSR | + S_IRUSR; + let write_val = "hello".as_bytes().to_owned(); + let write_buf = slice_to_uv_buf(write_val); + let write_buf_ptr: *Buf = &write_buf; + let open_req = FsRequest::new(); + do open_req.open(&loop_, &path, create_flags as int, mode as int) + |req, uverr| { + assert!(uverr.is_none()); + let fd = req.get_result(); + let buf = unsafe { *write_buf_ptr }; + let write_req = FsRequest::new(); + do write_req.write(&req.get_loop(), fd, buf, 0) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat = req.get_stat(); + let sz: uint = stat.st_size as uint; + assert!(sz > 0); + let close_req = FsRequest::new(); + do close_req.close(&loop_, fd) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let unlink_req = FsRequest::new(); + do unlink_req.unlink(&loop_, &path) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |_, uverr| { + // should cause an error because the + // file doesn't exist anymore + assert!(uverr.is_some()); + }; + }; + }; + }; + }; + }; + loop_.run(); + loop_.close(); + } + } + + #[test] + fn file_test_mk_rm_dir() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/mk_rm_dir"; + let mode = S_IWUSR | + S_IRUSR; + let mkdir_req = FsRequest::new(); + do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |req, uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat = req.get_stat(); + assert!(stat.is_dir()); + let rmdir_req = FsRequest::new(); + do rmdir_req.rmdir(&loop_, &path) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let stat_req = FsRequest::new(); + do stat_req.stat(&loop_, &path) |req, uverr| { + assert!(uverr.is_some()); + } + } + } + } + loop_.run(); + loop_.close(); + } + } + #[test] + fn file_test_mkdir_chokes_on_double_create() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/double_create_dir"; + let mode = S_IWUSR | + S_IRUSR; + let mkdir_req = FsRequest::new(); + do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + let mkdir_req = FsRequest::new(); + do mkdir_req.mkdir(&loop_, &path, mode as int) |req,uverr| { + assert!(uverr.is_some()); + let loop_ = req.get_loop(); + let stat = req.get_stat(); + let rmdir_req = FsRequest::new(); + do rmdir_req.rmdir(&loop_, &path) |req,uverr| { + assert!(uverr.is_none()); + let loop_ = req.get_loop(); + } + } + } + loop_.run(); + loop_.close(); + } + } + #[test] + fn file_test_rmdir_chokes_on_nonexistant_path() { + do run_in_bare_thread { + let mut loop_ = Loop::new(); + let path = "./tmp/never_existed_dir"; + let rmdir_req = FsRequest::new(); + do rmdir_req.rmdir(&loop_, &path) |req,uverr| { + assert!(uverr.is_some()); + } + loop_.run(); + loop_.close(); + } + } } diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index b930ea2437e..4307c57529b 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -32,11 +32,13 @@ use rt::uv::idle::IdleWatcher; use rt::uv::net::{UvIpv4SocketAddr, UvIpv6SocketAddr, accum_sockaddrs}; use rt::uv::addrinfo::GetAddrInfoRequest; use unstable::sync::Exclusive; +use path::Path; use super::super::io::support::PathLike; use libc::{lseek, off_t, O_CREAT, O_APPEND, O_TRUNC, O_RDWR, O_RDONLY, O_WRONLY, S_IRUSR, S_IWUSR}; use rt::io::{FileMode, FileAccess, OpenOrCreate, Open, Create, - CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite}; + CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite, + FileStat}; use task; #[cfg(test)] use container::Container; @@ -516,7 +518,6 @@ impl IoFactory for UvIoFactory { fn fs_from_raw_fd(&mut self, fd: c_int, close_on_drop: bool) -> ~RtioFileStream { let loop_ = Loop {handle: self.uv_loop().native_handle()}; - let fd = file::FileDescriptor(fd); let home = get_handle_to_current_scheduler!(); ~UvFileStream::new(loop_, fd, close_on_drop, home) as ~RtioFileStream } @@ -547,15 +548,16 @@ impl IoFactory for UvIoFactory { let path_cell = Cell::new(path); do task::unkillable { // FIXME(#8674) let scheduler: ~Scheduler = Local::take(); + let open_req = file::FsRequest::new(); do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); let path = path_cell.take(); - do file::FsRequest::open(self.uv_loop(), path, flags as int, create_mode as int) + do open_req.open(self.uv_loop(), path, flags as int, create_mode as int) |req,err| { if err.is_none() { let loop_ = Loop {handle: req.get_loop().native_handle()}; let home = get_handle_to_current_scheduler!(); - let fd = file::FileDescriptor(req.get_result()); + let fd = req.get_result() as c_int; let fs = ~UvFileStream::new( loop_, fd, true, home) as ~RtioFileStream; let res = Ok(fs); @@ -570,7 +572,7 @@ impl IoFactory for UvIoFactory { } }; }; - } + }; assert!(!result_cell.is_empty()); return result_cell.take(); } @@ -581,10 +583,11 @@ impl IoFactory for UvIoFactory { let path_cell = Cell::new(path); do task::unkillable { // FIXME(#8674) let scheduler: ~Scheduler = Local::take(); + let unlink_req = FsRequest::new(); do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); let path = path_cell.take(); - do file::FsRequest::unlink(self.uv_loop(), path) |_, err| { + do unlink_req.unlink(self.uv_loop(), path) |_, err| { let res = match err { None => Ok(()), Some(err) => Err(uv_error_to_io_error(err)) @@ -593,11 +596,48 @@ impl IoFactory for UvIoFactory { let scheduler: ~Scheduler = Local::take(); scheduler.resume_blocked_task_immediately(task_cell.take()); }; - }; + } } assert!(!result_cell.is_empty()); return result_cell.take(); } + fn fs_stat<P: PathLike>(&mut self, path: &P) -> Result<FileStat, IoError> { + use str::StrSlice; + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell<Result<FileStat, + IoError>> = &result_cell; + let path_cell = Cell::new(path); + do task::unkillable { // FIXME(#8674) + let scheduler: ~Scheduler = Local::take(); + let stat_req = file::FsRequest::new(); + do scheduler.deschedule_running_task_and_then |_, task| { + let task_cell = Cell::new(task); + let path = path_cell.take(); + let path_str = path.path_as_str(|p| p.to_owned()); + do stat_req.stat(self.uv_loop(), path) + |req,err| { + if err.is_none() { + let stat = req.get_stat(); + let res = Ok(FileStat { + path: Path(path_str), + is_file: stat.is_file(), + is_dir: stat.is_dir() + }); + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + } else { + let res = Err(uv_error_to_io_error(err.unwrap())); + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); + } + }; + }; + }; + assert!(!result_cell.is_empty()); + return result_cell.take(); + } fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError> { let result_cell = Cell::new_empty(); @@ -629,6 +669,9 @@ impl IoFactory for UvIoFactory { assert!(!result_cell.is_empty()); return result_cell.take(); } + //fn fs_fstat(&mut self, _fd: c_int) -> Result<FileStat, IoError> { + // Ok(FileStat) + //} } pub struct UvTcpListener { @@ -1173,7 +1216,7 @@ impl RtioTimer for UvTimer { pub struct UvFileStream { loop_: Loop, - fd: file::FileDescriptor, + fd: c_int, close_on_drop: bool, home: SchedHandle } @@ -1183,7 +1226,7 @@ impl HomingIO for UvFileStream { } impl UvFileStream { - fn new(loop_: Loop, fd: file::FileDescriptor, close_on_drop: bool, + fn new(loop_: Loop, fd: c_int, close_on_drop: bool, home: SchedHandle) -> UvFileStream { UvFileStream { loop_: loop_, @@ -1200,7 +1243,8 @@ impl UvFileStream { do scheduler.deschedule_running_task_and_then |_, task| { let buf = unsafe { slice_to_uv_buf(*buf_ptr) }; let task_cell = Cell::new(task); - do self_.fd.read(&self_.loop_, buf, offset) |req, uverr| { + let read_req = file::FsRequest::new(); + do read_req.read(&self_.loop_, self_.fd, buf, offset) |req, uverr| { let res = match uverr { None => Ok(req.get_result() as int), Some(err) => Err(uv_error_to_io_error(err)) @@ -1221,7 +1265,8 @@ impl UvFileStream { do scheduler.deschedule_running_task_and_then |_, task| { let buf = unsafe { slice_to_uv_buf(*buf_ptr) }; let task_cell = Cell::new(task); - do self_.fd.write(&self_.loop_, buf, offset) |_, uverr| { + let write_req = file::FsRequest::new(); + do write_req.write(&self_.loop_, self_.fd, buf, offset) |_, uverr| { let res = match uverr { None => Ok(()), Some(err) => Err(uv_error_to_io_error(err)) @@ -1238,7 +1283,7 @@ impl UvFileStream { Result<u64, IoError>{ #[fixed_stack_segment]; #[inline(never)]; unsafe { - match lseek((*self.fd), pos as off_t, whence) { + match lseek(self.fd, pos as off_t, whence) { -1 => { Err(IoError { kind: OtherIoError, @@ -1259,7 +1304,8 @@ impl Drop for UvFileStream { do self_.home_for_io_with_sched |self_, scheduler| { do scheduler.deschedule_running_task_and_then |_, task| { let task_cell = Cell::new(task); - do self_.fd.close(&self.loop_) |_,_| { + let close_req = file::FsRequest::new(); + do close_req.close(&self.loop_, self_.fd) |_,_| { let scheduler: ~Scheduler = Local::take(); scheduler.resume_blocked_task_immediately(task_cell.take()); }; diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index 8f3cef4d238..89ee54be349 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -96,6 +96,59 @@ pub type uv_fs_t = c_void; pub type uv_udp_send_t = c_void; pub type uv_getaddrinfo_t = c_void; +pub struct uv_timespec_t { + tv_sec: libc::c_long, + tv_nsec: libc::c_long +} + +pub struct uv_stat_t { + st_dev: libc::uint64_t, + st_mode: libc::uint64_t, + st_nlink: libc::uint64_t, + st_uid: libc::uint64_t, + st_gid: libc::uint64_t, + st_rdev: libc::uint64_t, + st_ino: libc::uint64_t, + st_size: libc::uint64_t, + st_blksize: libc::uint64_t, + st_blocks: libc::uint64_t, + st_flags: libc::uint64_t, + st_gen: libc::uint64_t, + st_atim: uv_timespec_t, + st_mtim: uv_timespec_t, + st_ctim: uv_timespec_t, + st_birthtim: uv_timespec_t +} + +impl uv_stat_t { + pub fn new() -> uv_stat_t { + uv_stat_t { + st_dev: 0, + st_mode: 0, + st_nlink: 0, + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_ino: 0, + st_size: 0, + st_blksize: 0, + st_blocks: 0, + st_flags: 0, + st_gen: 0, + st_atim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }, + st_mtim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }, + st_ctim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 }, + st_birthtim: uv_timespec_t { tv_sec: 0, tv_nsec: 0 } + } + } + pub fn is_file(&self) -> bool { + ((self.st_mode as c_int) & libc::S_IFMT) == libc::S_IFREG + } + pub fn is_dir(&self) -> bool { + ((self.st_mode as c_int) & libc::S_IFMT) == libc::S_IFDIR + } +} + #[cfg(stage0)] pub type uv_idle_cb = *u8; #[cfg(stage0)] @@ -736,6 +789,33 @@ pub unsafe fn fs_close(loop_ptr: *uv_loop_t, req: *uv_fs_t, fd: c_int, rust_uv_fs_close(loop_ptr, req, fd, cb) } +pub unsafe fn fs_stat(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_stat(loop_ptr, req, path, cb) +} +pub unsafe fn fs_fstat(loop_ptr: *uv_loop_t, req: *uv_fs_t, fd: c_int, cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_fstat(loop_ptr, req, fd, cb) +} +pub unsafe fn fs_mkdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, mode: int, + cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_mkdir(loop_ptr, req, path, mode as c_int, cb) +} +pub unsafe fn fs_rmdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, + cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_rmdir(loop_ptr, req, path, cb) +} +pub unsafe fn populate_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t) { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_populate_uv_stat(req_in, stat_out) +} pub unsafe fn fs_req_cleanup(req: *uv_fs_t) { #[fixed_stack_segment]; #[inline(never)]; @@ -928,7 +1008,14 @@ extern { buf: *c_void, len: c_uint, offset: i64, cb: *u8) -> c_int; fn rust_uv_fs_close(loop_ptr: *c_void, req: *uv_fs_t, fd: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_stat(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, cb: *u8) -> c_int; + fn rust_uv_fs_fstat(loop_ptr: *c_void, req: *uv_fs_t, fd: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_mkdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + mode: c_int, cb: *u8) -> c_int; + fn rust_uv_fs_rmdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + cb: *u8) -> c_int; fn rust_uv_fs_req_cleanup(req: *uv_fs_t); + fn rust_uv_populate_uv_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t); fn rust_uv_get_result_from_fs_req(req: *uv_fs_t) -> c_int; fn rust_uv_get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t; fn rust_uv_get_loop_from_getaddrinfo_req(req: *uv_fs_t) -> *uv_loop_t; |
