From af650572e0fceb94b387db50ee31f19ee000fe4b Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 26 Aug 2013 07:24:10 -0700 Subject: 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... --- src/rt/rust_uv.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'src/rt/rust_uv.cpp') diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index bfdf0e67a9b..ebc76c84ec9 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -551,3 +551,45 @@ extern "C" uv_loop_t* rust_uv_get_loop_from_getaddrinfo_req(uv_getaddrinfo_t* req) { return req->loop; } + +extern "C" int +rust_uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + return uv_fs_stat(loop, req, path, cb); +} +extern "C" int +rust_uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + return uv_fs_fstat(loop, req, file, cb); +} + +extern "C" void +rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_stat_t* stat_out) { + stat_out->st_dev = req_in->statbuf.st_dev; + stat_out->st_mode = req_in->statbuf.st_mode; + stat_out->st_nlink = req_in->statbuf.st_nlink; + stat_out->st_uid = req_in->statbuf.st_uid; + stat_out->st_gid = req_in->statbuf.st_gid; + stat_out->st_rdev = req_in->statbuf.st_rdev; + stat_out->st_ino = req_in->statbuf.st_ino; + stat_out->st_size = req_in->statbuf.st_size; + stat_out->st_blksize = req_in->statbuf.st_blksize; + stat_out->st_blocks = req_in->statbuf.st_blocks; + stat_out->st_flags = req_in->statbuf.st_flags; + stat_out->st_gen = req_in->statbuf.st_gen; + stat_out->st_atim.tv_sec = req_in->statbuf.st_atim.tv_sec; + stat_out->st_atim.tv_nsec = req_in->statbuf.st_atim.tv_nsec; + stat_out->st_mtim.tv_sec = req_in->statbuf.st_mtim.tv_sec; + stat_out->st_mtim.tv_nsec = req_in->statbuf.st_mtim.tv_nsec; + stat_out->st_ctim.tv_sec = req_in->statbuf.st_ctim.tv_sec; + stat_out->st_ctim.tv_nsec = req_in->statbuf.st_ctim.tv_nsec; + stat_out->st_birthtim.tv_sec = req_in->statbuf.st_birthtim.tv_sec; + stat_out->st_birthtim.tv_nsec = req_in->statbuf.st_birthtim.tv_nsec; +} + +extern "C" int +rust_uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { + return uv_fs_mkdir(loop, req, path, mode, cb); +} +extern "C" int +rust_uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + return uv_fs_rmdir(loop, req, path, cb); +} -- cgit 1.4.1-3-g733a5 From 055488df1a6a4500de565ac2531d7bc42dd02f83 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 2 Sep 2013 20:23:12 -0700 Subject: merge cleanup --- src/libstd/rt/uv/file.rs | 3 ++- src/rt/rust_uv.cpp | 42 +++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 22 deletions(-) (limited to 'src/rt/rust_uv.cpp') diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 32e248254f4..850b28718d0 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -183,8 +183,9 @@ impl FsRequest { // accessors/utility funcs fn sync_cleanup(self, result: c_int) -> Result { + let loop_ = self.get_loop().native_handle(); self.cleanup_and_delete(); - match status_to_maybe_uv_error(result as i32) { + match status_to_maybe_uv_error_with_loop(loop_,result as i32) { Some(err) => Err(err), None => Ok(result) } diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index ebc76c84ec9..5756ffb0de0 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -562,27 +562,27 @@ rust_uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { } extern "C" void -rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_stat_t* stat_out) { - stat_out->st_dev = req_in->statbuf.st_dev; - stat_out->st_mode = req_in->statbuf.st_mode; - stat_out->st_nlink = req_in->statbuf.st_nlink; - stat_out->st_uid = req_in->statbuf.st_uid; - stat_out->st_gid = req_in->statbuf.st_gid; - stat_out->st_rdev = req_in->statbuf.st_rdev; - stat_out->st_ino = req_in->statbuf.st_ino; - stat_out->st_size = req_in->statbuf.st_size; - stat_out->st_blksize = req_in->statbuf.st_blksize; - stat_out->st_blocks = req_in->statbuf.st_blocks; - stat_out->st_flags = req_in->statbuf.st_flags; - stat_out->st_gen = req_in->statbuf.st_gen; - stat_out->st_atim.tv_sec = req_in->statbuf.st_atim.tv_sec; - stat_out->st_atim.tv_nsec = req_in->statbuf.st_atim.tv_nsec; - stat_out->st_mtim.tv_sec = req_in->statbuf.st_mtim.tv_sec; - stat_out->st_mtim.tv_nsec = req_in->statbuf.st_mtim.tv_nsec; - stat_out->st_ctim.tv_sec = req_in->statbuf.st_ctim.tv_sec; - stat_out->st_ctim.tv_nsec = req_in->statbuf.st_ctim.tv_nsec; - stat_out->st_birthtim.tv_sec = req_in->statbuf.st_birthtim.tv_sec; - stat_out->st_birthtim.tv_nsec = req_in->statbuf.st_birthtim.tv_nsec; +rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_statbuf_t* stat_out) { + stat_out->st_dev = ((uv_statbuf_t*)req_in->ptr)->st_dev; + stat_out->st_mode = ((uv_statbuf_t*)req_in->ptr)->st_mode; + stat_out->st_nlink = ((uv_statbuf_t*)req_in->ptr)->st_nlink; + stat_out->st_uid = ((uv_statbuf_t*)req_in->ptr)->st_uid; + stat_out->st_gid = ((uv_statbuf_t*)req_in->ptr)->st_gid; + stat_out->st_rdev = ((uv_statbuf_t*)req_in->ptr)->st_rdev; + stat_out->st_ino = ((uv_statbuf_t*)req_in->ptr)->st_ino; + stat_out->st_size = ((uv_statbuf_t*)req_in->ptr)->st_size; + stat_out->st_blksize = ((uv_statbuf_t*)req_in->ptr)->st_blksize; + stat_out->st_blocks = ((uv_statbuf_t*)req_in->ptr)->st_blocks; + //stat_out->st_flags = ((uv_statbuf_t*)req_in->ptr)->st_flags; + //stat_out->st_gen = ((uv_statbuf_t*)req_in->ptr)->st_gen; + stat_out->st_atim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_atim.tv_sec; + stat_out->st_atim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_atim.tv_nsec; + stat_out->st_mtim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_mtim.tv_sec; + stat_out->st_mtim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_mtim.tv_nsec; + stat_out->st_ctim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_ctim.tv_sec; + stat_out->st_ctim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_ctim.tv_nsec; + //stat_out->st_birthtim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_birthtim.tv_sec; + //stat_out->st_birthtim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_birthtim.tv_nsec; } extern "C" int -- cgit 1.4.1-3-g733a5 From b49fc4cf4eb7299a08d83ed8880d1002ecef9257 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Sat, 14 Sep 2013 09:33:53 -0700 Subject: std: adding file::{stat,mkdir,rmdir}, FileInfo and FileReader/FileWriter add ignores for win32 tests on previous file io stuff... --- src/libstd/rt/io/file.rs | 284 ++++++++++++++++++++++++++++++++--------------- src/libstd/rt/rtio.rs | 2 + src/libstd/rt/uv/file.rs | 6 +- src/libstd/rt/uv/uvio.rs | 104 ++++++++++------- src/libstd/rt/uv/uvll.rs | 4 +- src/rt/rust_uv.cpp | 42 +++---- 6 files changed, 290 insertions(+), 152 deletions(-) (limited to 'src/rt/rust_uv.cpp') diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index ee102c3a97f..4968f327602 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -12,7 +12,7 @@ use prelude::*; use super::support::PathLike; use super::{Reader, Writer, Seek}; use super::{SeekStyle,SeekSet, SeekCur, SeekEnd, - Open, Read, Create, ReadWrite}; + Open, Read, Write, Create, ReadWrite}; use rt::rtio::{RtioFileStream, IoFactory, IoFactoryObject}; use rt::io::{io_error, read_error, EndOfFile, FileMode, FileAccess, FileStat}; @@ -57,26 +57,108 @@ pub fn unlink(path: &P) { } } -/// Abstraction representing *positional* access to a file. In this case, -/// *positional* refers to it keeping an encounter *cursor* of where in the -/// file a subsequent `read` or `write` will begin from. Users of a `FileStream` -/// can `seek` to move the cursor to a given location *within the bounds of the -/// file* and can ask to have the `FileStream` `tell` them the location, in -/// bytes, of the cursor. -/// -/// This abstraction is roughly modeled on the access workflow as represented -/// by `open(2)`, `read(2)`, `write(2)` and friends. +/// Create a new directory with default permissions (process user +/// has read/write privs) +pub fn mkdir(path: &P) { + let mkdir_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_mkdir(path) + }; + match mkdir_result { + Ok(_) => (), + Err(ioerr) => { + io_error::cond.raise(ioerr); + } + } +} +/// Removes a directory +pub fn rmdir(path: &P) { + let rmdir_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_rmdir(path) + }; + match rmdir_result { + Ok(_) => (), + Err(ioerr) => { + io_error::cond.raise(ioerr); + } + } +} + +/// Given a `rt::io::support::PathLike`, query the file system to get +/// information about a file, directory, etc. /// -/// The `open` and `unlink` static methods are provided to manage creation/removal -/// of files. All other methods operatin on an instance of `FileStream`. +/// Returns a `Some(PathInfo)` on success, and raises a `rt::io::IoError` condition +/// on failure and returns `None`. +pub fn stat(path: &P) -> Option { + 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 + } + } +} + +/// Read-only view of file +pub struct FileReader { priv stream: FileStream } + +impl Reader for FileReader { + fn read(&mut self, buf: &mut [u8]) -> Option { + self.stream.read(buf) + } + + fn eof(&mut self) -> bool { + self.stream.eof() + } +} + +impl Seek for FileReader { + fn tell(&self) -> u64 { + self.stream.tell() + } + + fn seek(&mut self, pos: i64, style: SeekStyle) { + self.stream.seek(pos, style); + } +} + +/// Write-only view of a file +pub struct FileWriter { priv stream: FileStream } + +impl Writer for FileWriter { + fn write(&mut self, buf: &[u8]) { + self.stream.write(buf); + } + + fn flush(&mut self) { + self.stream.flush(); + } +} + +impl Seek for FileWriter { + fn tell(&self) -> u64 { + self.stream.tell() + } + + fn seek(&mut self, pos: i64, style: SeekStyle) { + self.stream.seek(pos, style); + } +} + +/// Internal representation of a FileStream, used to consolidate functionality +/// exposed in the public API pub struct FileStream { fd: ~RtioFileStream, last_nread: int, } -impl FileStream { -} - impl Reader for FileStream { fn read(&mut self, buf: &mut [u8]) -> Option { match self.fd.read(buf) { @@ -148,69 +230,85 @@ impl Seek for FileStream { } } -pub struct FileInfo(Path); - -/// FIXME: DOCS -impl<'self> FileInfo { - pub fn new(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 { - do io_error::cond.trap(|_| { +/// Represents passive information about a file (primarily exposed +/// via the `stat()` method. Also provides methods for opening +/// a file in various modes/permissions. +pub trait FileInfo<'self> { + /// Get the filesystem path that this `FileInfo` points at, + /// whether it is valid or not. This way, it can be used to + /// to specify a file path of a non-existent file which it + /// later create + fn get_file_path(&'self self) -> &'self Path; + + /// Ask the operating system for information about the file + fn stat(&self) -> Option { + use mod_stat = super::file::stat; + do read_error::cond.trap(|_| { // FIXME: can we do something more useful here? }).inside { - stat(self.get_path()) + mod_stat(self.get_file_path()) } } - pub fn exists(&self) -> bool { + + /// returns `true` if the location pointed at by the enclosing + /// exists on the filesystem + fn file_exists(&self) -> bool { match self.stat() { - Some(s) => { - match s.is_file { - true => { - true - }, - false => { - // FIXME: raise condition? - false - } - } - }, + Some(_) => true, None => false } } - pub fn is_file(&self) -> bool { + + /// Whether the underlying implemention (be it a file path + /// or active file descriptor) is a "regular file". Will return + /// false for paths to non-existent locations or directories or + /// other non-regular files (named pipes, etc). + fn is_file(&self) -> bool { match self.stat() { Some(s) => s.is_file, - None => { - // FIXME: raise condition - false - } + None => false } } - pub fn open(&self, mode: FileMode, access: FileAccess) -> Option { - match self.is_file() { - true => { - open(self.get_path(), mode, access) + + /// Attempts to open a regular file for reading/writing based + /// on provided inputs + fn open_stream(&self, mode: FileMode, access: FileAccess) -> Option { + match self.stat() { + Some(s) => match s.is_file { + true => open(self.get_file_path(), mode, access), + false => None // FIXME: raise condition, not a regular file.. }, - false => { - // FIXME: raise condition - None - } + None => open(self.get_file_path(), mode, access) + } + } + /// Attempts to open a regular file for reading-only based + /// on provided inputs + fn open_reader(&self, mode: FileMode) -> Option { + match self.open_stream(mode, Read) { + Some(s) => Some(FileReader { stream: s}), + None => None + } + } + + /// Attempts to open a regular file for writing-only based + /// on provided inputs + fn open_writer(&self, mode: FileMode) -> Option { + match self.open_stream(mode, Write) { + Some(s) => Some(FileWriter { stream: s}), + None => 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); + + /// Attempt to remove a file from the filesystem, pending the closing + /// of any open file descriptors pointing to the file + fn unlink(&self) { + unlink(self.get_file_path()); + } +} + +/// `FileInfo` implementation for `Path`s +impl<'self> FileInfo<'self> for Path { + fn get_file_path(&'self self) -> &'self Path { self } } /* @@ -244,27 +342,6 @@ impl DirectoryInfo<'self> { } */ -/// 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(path: &P) -> Option { - 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"; @@ -412,7 +489,7 @@ fn file_test_io_seek_and_write_impl() { read_stream.read(read_mem); } unlink(filename); - let read_str = str::from_bytes(read_mem); + let read_str = str::from_utf8(read_mem); assert!(read_str == final_msg.to_owned()); } } @@ -463,8 +540,9 @@ fn file_test_io_seek_shakedown() { } #[test] +#[ignore(cfg(windows))] // FIXME #8810 fn file_test_stat_is_correct_on_is_file() { - do run_in_newsched_task { + do run_in_mt_newsched_task { let filename = &Path("./tmp/file_stat_correct_on_is_file.txt"); { let mut fs = open(filename, Create, ReadWrite).unwrap(); @@ -476,20 +554,48 @@ fn file_test_stat_is_correct_on_is_file() { None => fail!("shouldn't happen") }; assert!(stat_res.is_file); + unlink(filename); } } #[test] +#[ignore(cfg(windows))] // FIXME #8810 fn file_test_stat_is_correct_on_is_dir() { - //assert!(false); + do run_in_mt_newsched_task { + let filename = &Path("./tmp/file_stat_correct_on_is_dir"); + mkdir(filename); + let stat_res = match stat(filename) { + Some(s) => s, + None => fail!("shouldn't happen") + }; + assert!(stat_res.is_dir); + rmdir(filename); + } } #[test] +#[ignore(cfg(windows))] // FIXME #8810 fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { - //assert!(false); + do run_in_mt_newsched_task { + let dir = &Path("./tmp/fileinfo_false_on_dir"); + mkdir(dir); + assert!(dir.is_file() == false); + rmdir(dir); + } } #[test] +#[ignore(cfg(windows))] // FIXME #8810 fn file_test_fileinfo_check_exists_before_and_after_file_creation() { - //assert!(false); + do run_in_mt_newsched_task { + let file = &Path("./tmp/fileinfo_check_exists_b_and_a.txt"); + { + let msg = "foo".as_bytes(); + let mut w = file.open_writer(Create); + w.write(msg); + } + assert!(file.file_exists()); + file.unlink(); + assert!(!file.file_exists()); + } } diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index f08949e9165..0abf81f62de 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -76,6 +76,8 @@ pub trait IoFactory { fn get_host_addresses(&mut self, host: &str) -> Result<~[IpAddr], IoError>; fn fs_stat(&mut self, path: &P) -> Result; //fn fs_fstat(&mut self, fd: c_int) -> Result; + fn fs_mkdir(&mut self, path: &P) -> Result<(), IoError>; + fn fs_rmdir(&mut self, path: &P) -> Result<(), IoError>; } pub trait RtioStream { diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 850b28718d0..34f87e3601e 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -183,9 +183,8 @@ impl FsRequest { // accessors/utility funcs fn sync_cleanup(self, result: c_int) -> Result { - let loop_ = self.get_loop().native_handle(); self.cleanup_and_delete(); - match status_to_maybe_uv_error_with_loop(loop_,result as i32) { + match status_to_maybe_uv_error(result as i32) { Some(err) => Err(err), None => Ok(result) } @@ -261,6 +260,8 @@ fn sync_cleanup(result: int) match status_to_maybe_uv_error(result as i32) { Some(err) => Err(err), None => Ok(result) + } +} extern fn compl_cb(req: *uv_fs_t) { let mut req: FsRequest = NativeHandle::from_native_handle(req); @@ -522,6 +523,7 @@ mod test { assert!(uverr.is_none()); let loop_ = req.get_loop(); let stat = req.get_stat(); + naive_print(&loop_, fmt!("%?", stat)); assert!(stat.is_dir()); let rmdir_req = FsRequest::new(); do rmdir_req.rmdir(&loop_, &path) |req,uverr| { diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index 4307c57529b..88d168c85d2 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -35,7 +35,7 @@ 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}; + S_IRUSR, S_IWUSR, S_IRWXU}; use rt::io::{FileMode, FileAccess, OpenOrCreate, Open, Create, CreateOrTruncate, Append, Truncate, Read, Write, ReadWrite, FileStat}; @@ -413,6 +413,36 @@ impl UvIoFactory { } } +/// Helper for a variety of simple uv_fs_* functions that +/// have no ret val +fn uv_fs_helper(loop_: &mut Loop, path: &P, + cb: ~fn(&mut FsRequest, &mut Loop, &P, + ~fn(&FsRequest, Option))) + -> Result<(), IoError> { + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell> = &result_cell; + let path_cell = Cell::new(path); + do task::unkillable { // FIXME(#8674) + let scheduler: ~Scheduler = Local::take(); + let mut new_req = FsRequest::new(); + do scheduler.deschedule_running_task_and_then |_, task| { + let task_cell = Cell::new(task); + let path = path_cell.take(); + do cb(&mut new_req, loop_, path) |_, err| { + let res = match err { + None => Ok(()), + Some(err) => Err(uv_error_to_io_error(err)) + }; + 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(); +} + impl IoFactory for UvIoFactory { // Connect to an address and return a new stream // NB: This blocks the task waiting on the connection. @@ -578,28 +608,11 @@ impl IoFactory for UvIoFactory { } fn fs_unlink(&mut self, path: &P) -> Result<(), IoError> { - let result_cell = Cell::new_empty(); - let result_cell_ptr: *Cell> = &result_cell; - 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 unlink_req.unlink(self.uv_loop(), path) |_, err| { - let res = match err { - None => Ok(()), - Some(err) => Err(uv_error_to_io_error(err)) - }; - unsafe { (*result_cell_ptr).put_back(res); } - let scheduler: ~Scheduler = Local::take(); - scheduler.resume_blocked_task_immediately(task_cell.take()); - }; - } + do uv_fs_helper(self.uv_loop(), path) |unlink_req, l, p, cb| { + do unlink_req.unlink(l, p) |req, err| { + cb(req, err) + }; } - assert!(!result_cell.is_empty()); - return result_cell.take(); } fn fs_stat(&mut self, path: &P) -> Result { use str::StrSlice; @@ -616,22 +629,22 @@ impl IoFactory for UvIoFactory { 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()); - } + let res = match err { + None => { + let stat = req.get_stat(); + Ok(FileStat { + path: Path(path_str), + is_file: stat.is_file(), + is_dir: stat.is_dir() + }) + }, + Some(e) => { + Err(uv_error_to_io_error(e)) + } + }; + unsafe { (*result_cell_ptr).put_back(res); } + let scheduler: ~Scheduler = Local::take(); + scheduler.resume_blocked_task_immediately(task_cell.take()); }; }; }; @@ -672,6 +685,21 @@ impl IoFactory for UvIoFactory { //fn fs_fstat(&mut self, _fd: c_int) -> Result { // Ok(FileStat) //} + fn fs_mkdir(&mut self, path: &P) -> Result<(), IoError> { + let mode = S_IRWXU as int; + do uv_fs_helper(self.uv_loop(), path) |mkdir_req, l, p, cb| { + do mkdir_req.mkdir(l, p, mode as int) |req, err| { + cb(req, err) + }; + } + } + fn fs_rmdir(&mut self, path: &P) -> Result<(), IoError> { + do uv_fs_helper(self.uv_loop(), path) |rmdir_req, l, p, cb| { + do rmdir_req.rmdir(l, p) |req, err| { + cb(req, err) + }; + } + } } pub struct UvTcpListener { diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index 89ee54be349..a2d1c48c3e1 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -142,10 +142,10 @@ impl uv_stat_t { } } pub fn is_file(&self) -> bool { - ((self.st_mode as c_int) & libc::S_IFMT) == libc::S_IFREG + ((self.st_mode) & libc::S_IFMT as libc::uint64_t) == libc::S_IFREG as libc::uint64_t } pub fn is_dir(&self) -> bool { - ((self.st_mode as c_int) & libc::S_IFMT) == libc::S_IFDIR + ((self.st_mode) & libc::S_IFMT as libc::uint64_t) == libc::S_IFDIR as libc::uint64_t } } diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index 5756ffb0de0..ebc76c84ec9 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -562,27 +562,27 @@ rust_uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { } extern "C" void -rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_statbuf_t* stat_out) { - stat_out->st_dev = ((uv_statbuf_t*)req_in->ptr)->st_dev; - stat_out->st_mode = ((uv_statbuf_t*)req_in->ptr)->st_mode; - stat_out->st_nlink = ((uv_statbuf_t*)req_in->ptr)->st_nlink; - stat_out->st_uid = ((uv_statbuf_t*)req_in->ptr)->st_uid; - stat_out->st_gid = ((uv_statbuf_t*)req_in->ptr)->st_gid; - stat_out->st_rdev = ((uv_statbuf_t*)req_in->ptr)->st_rdev; - stat_out->st_ino = ((uv_statbuf_t*)req_in->ptr)->st_ino; - stat_out->st_size = ((uv_statbuf_t*)req_in->ptr)->st_size; - stat_out->st_blksize = ((uv_statbuf_t*)req_in->ptr)->st_blksize; - stat_out->st_blocks = ((uv_statbuf_t*)req_in->ptr)->st_blocks; - //stat_out->st_flags = ((uv_statbuf_t*)req_in->ptr)->st_flags; - //stat_out->st_gen = ((uv_statbuf_t*)req_in->ptr)->st_gen; - stat_out->st_atim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_atim.tv_sec; - stat_out->st_atim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_atim.tv_nsec; - stat_out->st_mtim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_mtim.tv_sec; - stat_out->st_mtim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_mtim.tv_nsec; - stat_out->st_ctim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_ctim.tv_sec; - stat_out->st_ctim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_ctim.tv_nsec; - //stat_out->st_birthtim.tv_sec = ((uv_statbuf_t*)req_in->ptr)->st_birthtim.tv_sec; - //stat_out->st_birthtim.tv_nsec = ((uv_statbuf_t*)req_in->ptr)->st_birthtim.tv_nsec; +rust_uv_populate_uv_stat(uv_fs_t* req_in, uv_stat_t* stat_out) { + stat_out->st_dev = req_in->statbuf.st_dev; + stat_out->st_mode = req_in->statbuf.st_mode; + stat_out->st_nlink = req_in->statbuf.st_nlink; + stat_out->st_uid = req_in->statbuf.st_uid; + stat_out->st_gid = req_in->statbuf.st_gid; + stat_out->st_rdev = req_in->statbuf.st_rdev; + stat_out->st_ino = req_in->statbuf.st_ino; + stat_out->st_size = req_in->statbuf.st_size; + stat_out->st_blksize = req_in->statbuf.st_blksize; + stat_out->st_blocks = req_in->statbuf.st_blocks; + stat_out->st_flags = req_in->statbuf.st_flags; + stat_out->st_gen = req_in->statbuf.st_gen; + stat_out->st_atim.tv_sec = req_in->statbuf.st_atim.tv_sec; + stat_out->st_atim.tv_nsec = req_in->statbuf.st_atim.tv_nsec; + stat_out->st_mtim.tv_sec = req_in->statbuf.st_mtim.tv_sec; + stat_out->st_mtim.tv_nsec = req_in->statbuf.st_mtim.tv_nsec; + stat_out->st_ctim.tv_sec = req_in->statbuf.st_ctim.tv_sec; + stat_out->st_ctim.tv_nsec = req_in->statbuf.st_ctim.tv_nsec; + stat_out->st_birthtim.tv_sec = req_in->statbuf.st_birthtim.tv_sec; + stat_out->st_birthtim.tv_nsec = req_in->statbuf.st_birthtim.tv_nsec; } extern "C" int -- cgit 1.4.1-3-g733a5 From bf399d558e755b5964666892e9f95440ad6f8ef2 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Mon, 16 Sep 2013 13:25:10 -0700 Subject: std: bind uv_fs_readdir(), flesh out DirectoryInfo and docs/cleanup --- src/libstd/rt/io/file.rs | 85 +++++++++++++++++++++++++++++++++++++++++++----- src/libstd/rt/rtio.rs | 2 ++ src/libstd/rt/uv/file.rs | 50 +++++++++++++++++++++++----- src/libstd/rt/uv/uvio.rs | 38 ++++++++++++++++++++++ src/libstd/rt/uv/uvll.rs | 14 ++++++++ src/rt/rust_uv.cpp | 9 +++++ src/rt/rustrt.def.in | 2 ++ 7 files changed, 184 insertions(+), 16 deletions(-) (limited to 'src/rt/rust_uv.cpp') diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index af202a729e3..7aac79b964f 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -108,6 +108,22 @@ pub fn stat(path: &P) -> Option { } } +pub fn readdir(path: &P) -> Option<~[Path]> { + let readdir_result = unsafe { + let io: *mut IoFactoryObject = Local::unsafe_borrow(); + (*io).fs_readdir(path, 0) + }; + match readdir_result { + Ok(p) => { + Some(p) + }, + Err(ioerr) => { + io_error::cond.raise(ioerr); + None + } + } +} + /// Read-only view of file pub struct FileReader { priv stream: FileStream } @@ -272,6 +288,18 @@ pub trait FileSystemInfo { /// Represents passive information about a file (primarily exposed /// via the `stat()` method. Also provides methods for opening /// a file in various modes/permissions. +/// +/// # Example +/// +/// * Check if a file exists, reading from it if so +/// +/// let f = &Path("/some/file/path.txt"); +/// if f.exists() { +/// let reader = f.open_reader(Open); +/// let mut mem = [0u8, 8*64000]; +/// reader.read(mem); +/// // ... +/// } pub trait FileInfo : FileSystemInfo { /// Whether the underlying implemention (be it a file path, /// or something else) points at a "regular file" on the FS. Will return @@ -290,7 +318,7 @@ pub trait FileInfo : FileSystemInfo { match suppressed_stat(|| self.stat()) { Some(s) => match s.is_file { true => open(self.get_path(), mode, access), - false => None // FIXME: raise condition, not a regular file.. + false => None }, None => open(self.get_path(), mode, access) } @@ -320,13 +348,16 @@ pub trait FileInfo : FileSystemInfo { } } -/// `FileSystemInfo` implementation for `Path`s +/// `FileSystemInfo` implementation for `Path`s impl FileSystemInfo for Path { fn get_path<'a>(&'a self) -> &'a Path { self } } -/// `FileInfo` implementation for `Path`s +/// `FileInfo` implementation for `Path`s impl FileInfo for Path { } +/// Passive information about a directory on the filesystem. Includes +/// Convenience methods to iterate over a directory's contents (via `readdir`, as +/// as `mkdir` and `rmdir` operations. trait DirectoryInfo : FileSystemInfo { /// Whether the underlying implemention (be it a file path, /// or something else) points at a directory file" on the FS. Will return @@ -368,8 +399,9 @@ trait DirectoryInfo : FileSystemInfo { let ioerr = IoError { kind: MismatchedFileTypeForOperation, desc: "Cannot do rmdir() on a non-directory", - detail: - Some(fmt!("%s is a non-directory; can't rmdir it", self.get_path().to_str())) + detail: Some(fmt!( + "%s is a non-directory; can't rmdir it", + self.get_path().to_str())) }; io_error::cond.raise(ioerr); } @@ -383,14 +415,13 @@ trait DirectoryInfo : FileSystemInfo { }) } } - fn readdir(&self) -> ~[~str] { - ~[] + fn readdir(&self) -> Option<~[Path]> { + readdir(self.get_path()) } //fn get_subdirs(&self, filter: &str) -> ~[Path]; //fn get_files(&self, filter: &str) -> ~[Path]; } -/// FIXME: DOCS impl DirectoryInfo for Path { } fn file_test_smoke_test_impl() { @@ -663,3 +694,41 @@ fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { assert!(!dir.exists()); } } + +#[test] +fn file_test_directoryinfo_readdir() { + use str; + do run_in_mt_newsched_task { + let dir = &Path("./tmp/di_readdir"); + dir.mkdir(); + let prefix = "foo"; + for n in range(0,3) { + let f = dir.push(fmt!("%d.txt", n)); + let mut w = f.open_writer(Create); + let msg_str = (prefix + n.to_str().to_owned()).to_owned(); + let msg = msg_str.as_bytes(); + w.write(msg); + } + match dir.readdir() { + Some(files) => { + let mut mem = [0u8, .. 4]; + for f in files.iter() { + { + let n = f.filestem(); + let mut r = f.open_reader(Open); + r.read(mem); + let read_str = str::from_utf8(mem); + let expected = match n { + Some(n) => prefix+n, + None => fail!("really shouldn't happen..") + }; + assert!(expected == read_str); + } + f.unlink(); + } + }, + None => fail!("shouldn't happen") + } + dir.rmdir(); + } +} \ No newline at end of file diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index 0abf81f62de..568ea3317e5 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -78,6 +78,8 @@ pub trait IoFactory { //fn fs_fstat(&mut self, fd: c_int) -> Result; fn fs_mkdir(&mut self, path: &P) -> Result<(), IoError>; fn fs_rmdir(&mut self, path: &P) -> Result<(), IoError>; + fn fs_readdir(&mut self, path: &P, flags: c_int) -> + Result<~[Path], IoError>; } pub trait RtioStream { diff --git a/src/libstd/rt/uv/file.rs b/src/libstd/rt/uv/file.rs index 34f87e3601e..cd0a217cc45 100644 --- a/src/libstd/rt/uv/file.rs +++ b/src/libstd/rt/uv/file.rs @@ -17,6 +17,7 @@ use rt::uv::uvll; use rt::uv::uvll::*; use super::super::io::support::PathLike; use cast::transmute; +use libc; use libc::{c_int}; use option::{None, Some, Option}; @@ -28,14 +29,6 @@ pub struct RequestData { } impl FsRequest { - pub fn new_REFACTOR_ME(cb: Option) -> 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.install_req_data(cb); - fs_req - } - pub fn new() -> FsRequest { let fs_req = unsafe { malloc_req(UV_FS) }; assert!(fs_req.is_not_null()); @@ -180,6 +173,17 @@ impl FsRequest { }); } + pub fn readdir(self, loop_: &Loop, path: &P, + flags: c_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_readdir(loop_.native_handle(), + self.native_handle(), p, flags, complete_cb_ptr) + }) + }); + } + // accessors/utility funcs fn sync_cleanup(self, result: c_int) -> Result { @@ -235,6 +239,36 @@ impl FsRequest { stat } + pub fn get_ptr(&self) -> *libc::c_void { + unsafe { + uvll::get_ptr_from_fs_req(self.native_handle()) + } + } + + pub fn get_paths(&mut self) -> ~[~str] { + use str; + let ptr = self.get_ptr(); + match self.get_result() { + n if (n <= 0) => { + ~[] + }, + n => { + let n_len = n as uint; + // we pass in the len that uv tells us is there + // for the entries and we don't continue past that.. + // it appears that sometimes the multistring isn't + // correctly delimited and we stray into garbage memory? + // in any case, passing Some(n_len) fixes it and ensures + // good results + let raw_path_strs = unsafe { + str::raw::from_c_multistring(ptr as *libc::c_char, Some(n_len)) }; + let raw_len = raw_path_strs.len(); + assert_eq!(raw_len, n_len); + raw_path_strs + } + } + } + fn cleanup_and_delete(self) { unsafe { let data = uvll::get_data_for_req(self.native_handle()); diff --git a/src/libstd/rt/uv/uvio.rs b/src/libstd/rt/uv/uvio.rs index d701a87004c..2d024f04e1d 100644 --- a/src/libstd/rt/uv/uvio.rs +++ b/src/libstd/rt/uv/uvio.rs @@ -704,6 +704,44 @@ impl IoFactory for UvIoFactory { }; } } + fn fs_readdir(&mut self, path: &P, flags: c_int) -> + Result<~[Path], IoError> { + use str::StrSlice; + let result_cell = Cell::new_empty(); + let result_cell_ptr: *Cell> = &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.readdir(self.uv_loop(), path, flags) + |req,err| { + let res = match err { + None => { + let rel_paths = req.get_paths(); + let mut paths = ~[]; + for r in rel_paths.iter() { + paths.push(Path(path_str+"/"+*r)); + } + Ok(paths) + }, + Some(e) => { + Err(uv_error_to_io_error(e)) + } + }; + 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(); + } } pub struct UvTcpListener { diff --git a/src/libstd/rt/uv/uvll.rs b/src/libstd/rt/uv/uvll.rs index a2d1c48c3e1..42102a52e2e 100644 --- a/src/libstd/rt/uv/uvll.rs +++ b/src/libstd/rt/uv/uvll.rs @@ -811,6 +811,12 @@ pub unsafe fn fs_rmdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, rust_uv_fs_rmdir(loop_ptr, req, path, cb) } +pub unsafe fn fs_readdir(loop_ptr: *uv_loop_t, req: *uv_fs_t, path: *c_char, + flags: c_int, cb: *u8) -> c_int { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_fs_readdir(loop_ptr, req, path, flags, cb) +} pub unsafe fn populate_stat(req_in: *uv_fs_t, stat_out: *uv_stat_t) { #[fixed_stack_segment]; #[inline(never)]; @@ -828,6 +834,11 @@ pub unsafe fn get_result_from_fs_req(req: *uv_fs_t) -> c_int { rust_uv_get_result_from_fs_req(req) } +pub unsafe fn get_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void { + #[fixed_stack_segment]; #[inline(never)]; + + rust_uv_get_ptr_from_fs_req(req) +} pub unsafe fn get_loop_from_fs_req(req: *uv_fs_t) -> *uv_loop_t { #[fixed_stack_segment]; #[inline(never)]; @@ -1014,9 +1025,12 @@ extern { 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_readdir(loop_ptr: *c_void, req: *uv_fs_t, path: *c_char, + flags: c_int, 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_ptr_from_fs_req(req: *uv_fs_t) -> *libc::c_void; 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; diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp index ebc76c84ec9..9b460cffd74 100644 --- a/src/rt/rust_uv.cpp +++ b/src/rt/rust_uv.cpp @@ -542,6 +542,10 @@ extern "C" int rust_uv_get_result_from_fs_req(uv_fs_t* req) { return req->result; } +extern "C" void* +rust_uv_get_ptr_from_fs_req(uv_fs_t* req) { + return req->ptr; +} extern "C" uv_loop_t* rust_uv_get_loop_from_fs_req(uv_fs_t* req) { return req->loop; @@ -593,3 +597,8 @@ extern "C" int rust_uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { return uv_fs_rmdir(loop, req, path, cb); } + +extern "C" int +rust_uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { + return uv_fs_readdir(loop, req, path, flags, cb); +} diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index d6b0d7c7a9d..3be958837dc 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -113,6 +113,7 @@ rust_uv_fs_write rust_uv_fs_read rust_uv_fs_close rust_uv_get_result_from_fs_req +rust_uv_get_ptr_from_fs_req rust_uv_get_loop_from_fs_req rust_uv_fs_stat rust_uv_fs_fstat @@ -120,6 +121,7 @@ rust_uv_fs_req_cleanup rust_uv_populate_uv_stat rust_uv_fs_mkdir rust_uv_fs_rmdir +rust_uv_fs_readdir rust_dbg_lock_create rust_dbg_lock_destroy rust_dbg_lock_lock -- cgit 1.4.1-3-g733a5