From b49fc4cf4eb7299a08d83ed8880d1002ecef9257 Mon Sep 17 00:00:00 2001 From: Jeff Olson Date: Sat, 14 Sep 2013 09:33:53 -0700 Subject: [PATCH] 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 | 282 +++++++++++++++++++++++++++------------ 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, 289 insertions(+), 151 deletions(-) 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. +/// 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. /// -/// This abstraction is roughly modeled on the access workflow as represented -/// by `open(2)`, `read(2)`, `write(2)` and friends. -/// -/// 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); +/// 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; -/// 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(|_| { + /// 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) } } - //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); + /// 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 + } + } + + /// 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