diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2013-10-25 17:04:37 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2013-11-03 15:15:42 -0800 |
| commit | 9c1851019f1ef9511fa8731b8f1acb0796d1e97f (patch) | |
| tree | 0cd6d600bfc077e1d19722afdb042c9c016db621 /src/libstd/rt | |
| parent | 7bf58c2baaac3f7cb3c8e8d735b27ac9e7d3cd78 (diff) | |
| download | rust-9c1851019f1ef9511fa8731b8f1acb0796d1e97f.tar.gz rust-9c1851019f1ef9511fa8731b8f1acb0796d1e97f.zip | |
Remove all blocking std::os blocking functions
This commit moves all thread-blocking I/O functions from the std::os module. Their replacements can be found in either std::rt::io::file or in a hidden "old_os" module inside of native::file. I didn't want to outright delete these functions because they have a lot of special casing learned over time for each OS/platform, and I imagine that these will someday get integrated into a blocking implementation of IoFactory. For now, they're moved to a private module to prevent bitrot and still have tests to ensure that they work. I've also expanded the extensions to a few more methods defined on Path, most of which were previously defined in std::os but now have non-thread-blocking implementations as part of using the current IoFactory. The api of io::file is in flux, but I plan on changing it in the next commit as well. Closes #10057
Diffstat (limited to 'src/libstd/rt')
| -rw-r--r-- | src/libstd/rt/io/file.rs | 793 | ||||
| -rw-r--r-- | src/libstd/rt/io/mod.rs | 21 | ||||
| -rw-r--r-- | src/libstd/rt/io/native/file.rs | 483 | ||||
| -rw-r--r-- | src/libstd/rt/io/net/unix.rs | 3 | ||||
| -rw-r--r-- | src/libstd/rt/io/signal.rs | 7 | ||||
| -rw-r--r-- | src/libstd/rt/io/timer.rs | 1 | ||||
| -rw-r--r-- | src/libstd/rt/rtio.rs | 7 |
7 files changed, 900 insertions, 415 deletions
diff --git a/src/libstd/rt/io/file.rs b/src/libstd/rt/io/file.rs index 99a4a709504..b6c0b58434f 100644 --- a/src/libstd/rt/io/file.rs +++ b/src/libstd/rt/io/file.rs @@ -13,50 +13,43 @@ This module provides a set of functions and traits for working with regular files & directories on a filesystem. -At the top-level of the module are a set of freestanding functions, -associated with various filesystem operations. They all operate -on a `ToCStr` object. This trait is already defined for common -objects such as strings and `Path` instances. +At the top-level of the module are a set of freestanding functions, associated +with various filesystem operations. They all operate on a `Path` object. All operations in this module, including those as part of `FileStream` et al block the task during execution. Most will raise `std::rt::io::io_error` conditions in the event of failure. -Also included in this module are the `FileInfo` and `DirectoryInfo` traits. When -`use`'d alongside a value whose type implements them (A `std::path::Path` impl is -a part of this module), they expose a set of functions for operations against -a given file location, depending on whether the path already exists. Whenever -possible, the `{FileInfo, DirectoryInfo}` preserve the same semantics as their -free function counterparts. +Also included in this module is an implementation block on the `Path` object +defined in `std::path::Path`. The impl adds useful methods about inspecting the +metadata of a file. This includes getting the `stat` information, reading off +particular bits of it, etc. + */ -use prelude::*; use c_str::ToCStr; use super::{Reader, Writer, Seek}; -use super::{SeekStyle, Read, Write}; +use super::{SeekStyle, Read, Write, Open, CreateOrTruncate, + FileMode, FileAccess, FileStat, io_error, FilePermission}; use rt::rtio::{RtioFileStream, IoFactory, with_local_io}; -use rt::io::{io_error, EndOfFile, - FileMode, FileAccess, FileStat, IoError, - PathAlreadyExists, PathDoesntExist, - MismatchedFileTypeForOperation, ignore_io_error}; -use option::{Some, None}; -use path::Path; +use rt::io; +use option::{Some, None, Option}; +use result::{Ok, Err}; +use path; +use path::{Path, GenericPath}; /// Open a file for reading/writing, as indicated by `path`. /// /// # Example /// -/// use std; -/// use std::path::Path; -/// use std::rt::io::file::open; -/// use std::rt::io::{FileMode, FileAccess}; +/// use std::rt::{io, file, io_error}; /// -/// let p = &Path("/some/file/path.txt"); +/// let p = Path::new("/some/file/path.txt"); /// /// do io_error::cond.trap(|_| { /// // hoo-boy... /// }).inside { -/// let stream = match open(p, Create, ReadWrite) { +/// let stream = match file::open_stream(&p, io::Create, io::ReadWrite) { /// Some(s) => s, /// None => fail!("whoops! I'm sure this raised, anyways..") /// }; @@ -72,24 +65,23 @@ use path::Path; /// /// Note that, with this function, a `FileStream` is returned regardless of /// the access-limitations indicated by `FileAccess` (e.g. calling `write` on a -/// `FileStream` opened as `ReadOnly` will raise an `io_error` condition at runtime). If you -/// desire a more-correctly-constrained interface to files, use the -/// `{open_stream, open_reader, open_writer}` methods that are a part of `FileInfo` +/// `FileStream` opened as `ReadOnly` will raise an `io_error` condition at +/// runtime). If you desire a more-correctly-constrained interface to files, +/// use the `{open_stream, open, create}` methods that are a part of `Path`. /// /// # Errors /// -/// This function will raise an `io_error` condition under a number of different circumstances, -/// to include but not limited to: +/// This function will raise an `io_error` condition under a number of different +/// circumstances, to include but not limited to: /// -/// * Opening a file that already exists with `FileMode` of `Create` or vice versa (e.g. -/// opening a non-existant file with `FileMode` or `Open`) -/// * Attempting to open a file with a `FileAccess` that the user lacks permissions -/// for +/// * Opening a file that already exists with `FileMode` of `Create` or vice +/// versa (e.g. opening a non-existant file with `FileMode` or `Open`) +/// * Attempting to open a file with a `FileAccess` that the user lacks +/// permissions for /// * Filesystem-level errors (full disk, etc) -pub fn open<P: ToCStr>(path: &P, - mode: FileMode, - access: FileAccess - ) -> Option<FileStream> { +pub fn open_stream(path: &Path, + mode: FileMode, + access: FileAccess) -> Option<FileStream> { do with_local_io |io| { match io.fs_open(&path.to_c_str(), mode, access) { Ok(fd) => Some(FileStream { @@ -104,16 +96,32 @@ pub fn open<P: ToCStr>(path: &P, } } +/// Attempts to open a file in read-only mode. This function is equivalent to +/// `open_stream(path, Open, Read)`, and will raise all of the same errors that +/// `open_stream` does. +/// +/// For more information, see the `open_stream` function. +pub fn open(path: &Path) -> Option<FileReader> { + open_stream(path, Open, Read).map(|s| FileReader { stream: s }) +} + +/// Attempts to create a file in write-only mode. This function is equivalent to +/// `open_stream(path, CreateOrTruncate, Write)`, and will raise all of the +/// same errors that `open_stream` does. +/// +/// For more information, see the `open_stream` function. +pub fn create(path: &Path) -> Option<FileWriter> { + open_stream(path, CreateOrTruncate, Write).map(|s| FileWriter { stream: s }) +} + /// Unlink a file from the underlying filesystem. /// /// # Example /// -/// use std; -/// use std::path::Path; -/// use std::rt::io::file::unlink; +/// use std::rt::io::file; /// -/// let p = &Path("/some/file/path.txt"); -/// unlink(p); +/// let p = Path::new("/some/file/path.txt"); +/// file::unlink(&p); /// // if we made it here without failing, then the /// // unlink operation was successful /// @@ -123,12 +131,13 @@ pub fn open<P: ToCStr>(path: &P, /// /// # Errors /// -/// This function will raise an `io_error` condition if the user lacks permissions to -/// remove the file or if some other filesystem-level error occurs -pub fn unlink<P: ToCStr>(path: &P) { +/// This function will raise an `io_error` condition if the path points to a +/// directory, the user lacks permissions to remove the file, or if some other +/// filesystem-level error occurs. +pub fn unlink(path: &Path) { do with_local_io |io| { match io.fs_unlink(&path.to_c_str()) { - Ok(_) => Some(()), + Ok(()) => Some(()), Err(ioerr) => { io_error::cond.raise(ioerr); None @@ -141,21 +150,21 @@ pub fn unlink<P: ToCStr>(path: &P) { /// /// # Example /// -/// use std; -/// use std::path::Path; -/// use std::rt::io::file::mkdir; +/// use std::libc::S_IRWXU; +/// use std::rt::io::file; /// -/// let p = &Path("/some/dir"); -/// mkdir(p); +/// let p = Path::new("/some/dir"); +/// file::mkdir(&p, S_IRWXU as int); /// // If we got here, our directory exists! Horray! /// /// # Errors /// -/// This call will raise an `io_error` condition if the user lacks permissions to make a -/// new directory at the provided path, or if the directory already exists -pub fn mkdir<P: ToCStr>(path: &P) { +/// This call will raise an `io_error` condition if the user lacks permissions +/// to make a new directory at the provided path, or if the directory already +/// exists. +pub fn mkdir(path: &Path, mode: FilePermission) { do with_local_io |io| { - match io.fs_mkdir(&path.to_c_str()) { + match io.fs_mkdir(&path.to_c_str(), mode) { Ok(_) => Some(()), Err(ioerr) => { io_error::cond.raise(ioerr); @@ -169,19 +178,18 @@ pub fn mkdir<P: ToCStr>(path: &P) { /// /// # Example /// -/// use std; -/// use std::path::Path; -/// use std::rt::io::file::rmdir; +/// use std::rt::io::file; /// -/// let p = &Path("/some/dir"); -/// rmdir(p); +/// let p = Path::new("/some/dir"); +/// file::rmdir(&p); /// // good riddance, you mean ol' directory /// /// # Errors /// -/// This call will raise an `io_error` condition if the user lacks permissions to remove the -/// directory at the provided path, or if the directory isn't empty -pub fn rmdir<P: ToCStr>(path: &P) { +/// This call will raise an `io_error` condition if the user lacks permissions +/// to remove the directory at the provided path, or if the directory isn't +/// empty. +pub fn rmdir(path: &Path) { do with_local_io |io| { match io.fs_rmdir(&path.to_c_str()) { Ok(_) => Some(()), @@ -198,37 +206,31 @@ pub fn rmdir<P: ToCStr>(path: &P) { /// Given a path, query the file system to get information about a file, /// directory, etc. /// -/// Returns a `Some(std::rt::io::PathInfo)` on success +/// Returns a fully-filled out stat structure on succes, and on failure it will +/// return a dummy stat structure (it is expected that the condition raised is +/// handled as well). /// /// # Example /// -/// use std; -/// use std::path::Path; -/// use std::rt::io::file::stat; +/// use std::rt::io::{file, io_error}; /// -/// let p = &Path("/some/file/path.txt"); +/// let p = Path::new("/some/file/path.txt"); /// /// do io_error::cond.trap(|_| { /// // hoo-boy... /// }).inside { -/// let info = match stat(p) { -/// Some(s) => s, -/// None => fail!("whoops! I'm sure this raised, anyways.."); -/// } -/// if stat.is_file { +/// let info = file::stat(p); +/// if info.is_file { /// // just imagine the possibilities ... /// } -/// -/// // the file stream will be closed at the end of this block /// } -/// // .. /// /// # Errors /// /// This call will raise an `io_error` condition if the user lacks the requisite /// permissions to perform a `stat` call on the given path or if there is no /// entry in the filesystem at the provided path. -pub fn stat<P: ToCStr>(path: &P) -> Option<FileStat> { +pub fn stat(path: &Path) -> FileStat { do with_local_io |io| { match io.fs_stat(&path.to_c_str()) { Ok(p) => Some(p), @@ -237,20 +239,31 @@ pub fn stat<P: ToCStr>(path: &P) -> Option<FileStat> { None } } - } + }.unwrap_or_else(|| { + FileStat { + path: Path::new(path.to_c_str()), + is_file: false, + is_dir: false, + device: 0, + mode: 0, + inode: 0, + size: 0, + created: 0, + modified: 0, + accessed: 0, + } + }) } /// Retrieve a vector containing all entries within a provided directory /// /// # Example /// -/// use std; -/// use std::path::Path; -/// use std::rt::io::file::readdir; +/// use std::rt::io::file; /// /// fn visit_dirs(dir: &Path, cb: &fn(&Path)) { /// if dir.is_dir() { -/// let contents = dir.readdir(); +/// let contents = file::readdir(dir).unwrap(); /// for entry in contents.iter() { /// if entry.is_dir() { visit_dirs(entry, cb); } /// else { cb(entry); } @@ -261,10 +274,10 @@ pub fn stat<P: ToCStr>(path: &P) -> Option<FileStat> { /// /// # Errors /// -/// Will raise an `io_error` condition if the provided `path` doesn't exist, +/// Will raise an `io_error` condition if the provided `from` doesn't exist, /// the process lacks permissions to view the contents or if the `path` points /// at a non-directory file -pub fn readdir<P: ToCStr>(path: &P) -> Option<~[Path]> { +pub fn readdir(path: &Path) -> ~[Path] { do with_local_io |io| { match io.fs_readdir(&path.to_c_str(), 0) { Ok(p) => Some(p), @@ -273,66 +286,169 @@ pub fn readdir<P: ToCStr>(path: &P) -> Option<~[Path]> { None } } - } + }.unwrap_or_else(|| ~[]) } -/// Constrained version of `FileStream` that only exposes read-specific operations. +/// Rename a file or directory to a new name. /// -/// Can be retreived via `FileInfo.open_reader()`. -pub struct FileReader { priv stream: FileStream } +/// # Example +/// +/// use std::rt::io::file; +/// +/// file::rename(Path::new("foo"), Path::new("bar")); +/// // Oh boy, nothing was raised! +/// +/// # Errors +/// +/// Will raise an `io_error` condition if the provided `path` doesn't exist, +/// the process lacks permissions to view the contents, or if some other +/// intermittent I/O error occurs. +pub fn rename(from: &Path, to: &Path) { + do with_local_io |io| { + match io.fs_rename(&from.to_c_str(), &to.to_c_str()) { + Ok(()) => Some(()), + Err(ioerr) => { + io_error::cond.raise(ioerr); + None + } + } + }; +} -/// a `std::rt::io::Reader` trait impl for file I/O. -impl Reader for FileReader { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - self.stream.read(buf) +/// Copies the contents of one file to another. +/// +/// # Example +/// +/// use std::rt::io::file; +/// +/// file::copy(Path::new("foo.txt"), Path::new("bar.txt")); +/// // Oh boy, nothing was raised! +/// +/// # Errors +/// +/// Will raise an `io_error` condition if the provided `from` doesn't exist, +/// the process lacks permissions to view the contents, or if some other +/// intermittent I/O error occurs (such as `to` couldn't be created). +pub fn copy(from: &Path, to: &Path) { + let mut reader = match open(from) { Some(f) => f, None => return }; + let mut writer = match create(to) { Some(f) => f, None => return }; + let mut buf = [0, ..io::DEFAULT_BUF_SIZE]; + + loop { + match reader.read(buf) { + Some(amt) => writer.write(buf.slice_to(amt)), + None => break + } } - fn eof(&mut self) -> bool { - self.stream.eof() - } + // FIXME(#10131) this is an awful way to pull out the permission bits. + // If this comment is removed, then there should be a test + // asserting that permission bits are maintained using the + // permission interface created. + chmod(to, (from.stat().mode & 0xfff) as u32); } -/// a `std::rt::io::Seek` trait impl for file I/O. -impl Seek for FileReader { - fn tell(&self) -> u64 { - self.stream.tell() - } +// This function is not public because it's got a terrible interface for `mode` +// FIXME(#10131) +fn chmod(path: &Path, mode: u32) { + do with_local_io |io| { + match io.fs_chmod(&path.to_c_str(), mode) { + Ok(()) => Some(()), + Err(ioerr) => { + io_error::cond.raise(ioerr); + None + } + } + }; +} - fn seek(&mut self, pos: i64, style: SeekStyle) { - self.stream.seek(pos, style); +/// Recursively walk a directory structure. This function will call the +/// provided closure on all directories and files found inside the path +/// pointed to by `self`. If the closure returns `false`, then the iteration +/// will be short-circuited. +pub fn walk_dir(path: &Path, f: &fn(&Path) -> bool) -> bool { + let files = readdir(path); + files.iter().advance(|q| { + f(q) && (!q.is_dir() || walk_dir(q, |p| f(p))) + }) +} + +/// Recursively create a directory and all of its parent components if they +/// are missing. +/// +/// # Failure +/// +/// This function will raise on the `io_error` condition if an error +/// happens, see `file::mkdir` for more information about error conditions +/// and performance. +pub fn mkdir_recursive(path: &Path, mode: FilePermission) { + // tjc: if directory exists but with different permissions, + // should we return false? + if path.is_dir() { + return + } + if path.filename().is_some() { + mkdir_recursive(&path.dir_path(), mode); } + mkdir(path, mode) } -/// Constrained version of `FileStream` that only exposes write-specific operations. +/// Removes a directory at this path, after removing all its contents. Use +/// carefully! /// -/// Can be retreived via `FileInfo.open_writer()`. +/// # Failure +/// +/// This function will raise on the `io_error` condition if an error +/// happens. See `file::unlink` and `file::readdir` for possible error +/// conditions. +pub fn rmdir_recursive(path: &Path) { + do walk_dir(path) |inner| { + if inner.is_dir() { + rmdir_recursive(inner); + } else { + unlink(inner); + } + true + }; + // Directory should now be empty + rmdir(path); +} + +/// Constrained version of `FileStream` that only exposes read-specific +/// operations. +/// +/// Can be retreived via `Path.open()` or `file::open`. +pub struct FileReader { priv stream: FileStream } + +impl Reader for FileReader { + fn read(&mut self, buf: &mut [u8]) -> Option<uint> { 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, p: i64, s: SeekStyle) { self.stream.seek(p, s) } +} + +/// Constrained version of `FileStream` that only exposes write-specific +/// operations. +/// +/// Can be retreived via `Path.create()` or `file::create`. pub struct FileWriter { priv stream: FileStream } -/// a `std::rt::io::Writer` trait impl for file I/O. impl Writer for FileWriter { - fn write(&mut self, buf: &[u8]) { - self.stream.write(buf); - } - - fn flush(&mut self) { - self.stream.flush(); - } + fn write(&mut self, buf: &[u8]) { self.stream.write(buf); } + fn flush(&mut self) { self.stream.flush(); } } -/// a `std::rt::io::Seek` trait impl for file I/O. impl Seek for FileWriter { - fn tell(&self) -> u64 { - self.stream.tell() - } - - fn seek(&mut self, pos: i64, style: SeekStyle) { - self.stream.seek(pos, style); - } + fn tell(&self) -> u64 { self.stream.tell() } + fn seek(&mut self, p: i64, s: SeekStyle) { self.stream.seek(p, s); } } /// Unconstrained file access type that exposes read and write operations /// -/// Can be retreived via `file::open()` and `FileInfo.open_stream()`. +/// Can be retreived via `file::open()` and `Path.open_stream()`. /// /// # Errors /// @@ -341,13 +457,12 @@ impl Seek for FileWriter { /// time, via the `FileAccess` parameter to `file::open()`. /// /// For this reason, it is best to use the access-constrained wrappers that are -/// exposed via `FileInfo.open_reader()` and `FileInfo.open_writer()`. +/// exposed via `Path.open()` and `Path.create()`. pub struct FileStream { priv fd: ~RtioFileStream, priv last_nread: int, } -/// a `std::rt::io::Reader` trait impl for file I/O. impl Reader for FileStream { fn read(&mut self, buf: &mut [u8]) -> Option<uint> { match self.fd.read(buf) { @@ -360,7 +475,7 @@ impl Reader for FileStream { }, Err(ioerr) => { // EOF is indicated by returning None - if ioerr.kind != EndOfFile { + if ioerr.kind != io::EndOfFile { io_error::cond.raise(ioerr); } return None; @@ -368,12 +483,9 @@ impl Reader for FileStream { } } - fn eof(&mut self) -> bool { - self.last_nread == 0 - } + fn eof(&mut self) -> bool { self.last_nread == 0 } } -/// a `std::rt::io::Writer` trait impl for file I/O. impl Writer for FileStream { fn write(&mut self, buf: &[u8]) { match self.fd.write(buf) { @@ -385,7 +497,6 @@ impl Writer for FileStream { } } -/// a `std::rt::io:Seek` trait impl for file I/O. impl Seek for FileStream { fn tell(&self) -> u64 { let res = self.fd.tell(); @@ -412,166 +523,40 @@ impl Seek for FileStream { } } -/// Shared functionality between `FileInfo` and `DirectoryInfo` -pub trait FileSystemInfo { - /// Get the filesystem path that this instance points at, - /// whether it is valid or not. In this way, it can be used to - /// to specify a path of a non-existent file which it - /// later creates - fn get_path<'a>(&'a self) -> &'a Path; - - /// Get information on the file, directory, etc at the provided path +impl path::Path { + /// Get information on the file, directory, etc at this path. /// /// Consult the `file::stat` documentation for more info. /// - /// This call preserves identical runtime/error semantics with `file::stat` - fn stat(&self) -> Option<FileStat> { - stat(self.get_path()) - } + /// This call preserves identical runtime/error semantics with `file::stat`. + pub fn stat(&self) -> FileStat { stat(self) } - /// Boolean value indicator whether the underlying file exists on the filesystem + /// Boolean value indicator whether the underlying file exists on the local + /// filesystem. This will return true if the path points to either a + /// directory or a file. /// /// # Errors /// /// Will not raise a condition - fn exists(&self) -> bool { - match ignore_io_error(|| self.stat()) { - Some(_) => true, - None => false - } + pub fn exists(&self) -> bool { + io::result(|| self.stat()).is_ok() } -} - -/// Represents a file, whose underlying path may or may not be valid -/// -/// # Example -/// -/// * Check if a file exists, reading from it if so -/// -/// ```rust -/// use std; -/// use std::path::Path; -/// use std::rt::io::file::{FileInfo, FileReader}; -/// -/// 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); -/// // ... -/// } -/// ``` -/// -/// * Is the given path a file? -/// -/// ```rust -/// let f = get_file_path_from_wherever(); -/// match f.is_file() { -/// true => doing_something_with_a_file(f), -/// _ => {} -/// } -/// ``` -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 - /// false for paths to non-existent locations or directories or - /// other non-regular files (named pipes, etc). + /// Whether the underlying implemention (be it a file path, or something + /// else) points at a "regular file" on the FS. Will return false for paths + /// to non-existent locations or directories or other non-regular files + /// (named pipes, etc). /// /// # Errors /// /// Will not raise a condition - fn is_file(&self) -> bool { - match ignore_io_error(|| self.stat()) { - Some(s) => s.is_file, - None => false - } - } - - /// Attempts to open a regular file for reading/writing based - /// on provided inputs - /// - /// See `file::open` for more information on runtime semantics and error conditions - fn open_stream(&self, mode: FileMode, access: FileAccess) -> Option<FileStream> { - match ignore_io_error(|| self.stat()) { - Some(s) => match s.is_file { - true => open(self.get_path(), mode, access), - false => None - }, - None => open(self.get_path(), mode, access) + pub fn is_file(&self) -> bool { + match io::result(|| self.stat()) { + Ok(s) => s.is_file, + Err(*) => false } } - /// Attempts to open a regular file in read-only mode, based - /// on provided inputs - /// - /// See `file::open` for more information on runtime semantics and error conditions - fn open_reader(&self, mode: FileMode) -> Option<FileReader> { - match self.open_stream(mode, Read) { - Some(s) => Some(FileReader { stream: s}), - None => None - } - } - - /// Attempts to open a regular file in write-only mode, based - /// on provided inputs - /// - /// See `file::open` for more information on runtime semantics and error conditions - fn open_writer(&self, mode: FileMode) -> Option<FileWriter> { - match self.open_stream(mode, Write) { - Some(s) => Some(FileWriter { stream: s}), - None => None - } - } - - /// Attempt to remove a file from the filesystem - /// - /// See `file::unlink` for more information on runtime semantics and error conditions - fn unlink(&self) { - unlink(self.get_path()); - } -} - -/// `FileSystemInfo` implementation for `Path`s -impl FileSystemInfo for Path { - fn get_path<'a>(&'a self) -> &'a Path { self } -} - -/// `FileInfo` implementation for `Path`s -impl FileInfo for Path { } - -/// Represents a directory, whose underlying path may or may not be valid -/// -/// # Example -/// -/// * Check if a directory exists, `mkdir`'ing it if not -/// -/// ```rust -/// use std; -/// use std::path::Path; -/// use std::rt::io::file::{DirectoryInfo}; -/// -/// let dir = &Path("/some/dir"); -/// if !dir.exists() { -/// dir.mkdir(); -/// } -/// ``` -/// -/// * Is the given path a directory? If so, iterate on its contents -/// -/// ```rust -/// fn visit_dirs(dir: &Path, cb: &fn(&Path)) { -/// if dir.is_dir() { -/// let contents = dir.readdir(); -/// for entry in contents.iter() { -/// if entry.is_dir() { visit_dirs(entry, cb); } -/// else { cb(entry); } -/// } -/// } -/// else { fail!("nope"); } -/// } -/// ``` -pub trait DirectoryInfo : FileSystemInfo { /// Whether the underlying implemention (be it a file path, /// or something else) is pointing at a directory in the underlying FS. /// Will return false for paths to non-existent locations or if the item is @@ -580,109 +565,50 @@ pub trait DirectoryInfo : FileSystemInfo { /// # Errors /// /// Will not raise a condition - fn is_dir(&self) -> bool { - match ignore_io_error(|| self.stat()) { - Some(s) => s.is_dir, - None => false + pub fn is_dir(&self) -> bool { + match io::result(|| self.stat()) { + Ok(s) => s.is_dir, + Err(*) => false } } - - /// Create a directory at the location pointed to by the - /// type underlying the given `DirectoryInfo`. - /// - /// # Errors - /// - /// This method will raise a `PathAlreadyExists` kind of `io_error` condition - /// if the provided path exists - /// - /// See `file::mkdir` for more information on runtime semantics and error conditions - fn mkdir(&self) { - match ignore_io_error(|| self.stat()) { - Some(_) => { - let path = self.get_path(); - io_error::cond.raise(IoError { - kind: PathAlreadyExists, - desc: "Path already exists", - detail: - Some(format!("{} already exists; can't mkdir it", - path.display())) - }) - }, - None => mkdir(self.get_path()) - } - } - - /// Remove a directory at the given location. - /// - /// # Errors - /// - /// This method will raise a `PathDoesntExist` kind of `io_error` condition - /// if the provided path exists. It will raise a `MismatchedFileTypeForOperation` - /// kind of `io_error` condition if the provided path points at any - /// non-directory file type - /// - /// See `file::rmdir` for more information on runtime semantics and error conditions - fn rmdir(&self) { - match ignore_io_error(|| self.stat()) { - Some(s) => { - match s.is_dir { - true => rmdir(self.get_path()), - false => { - let path = self.get_path(); - let ioerr = IoError { - kind: MismatchedFileTypeForOperation, - desc: "Cannot do rmdir() on a non-directory", - detail: Some(format!( - "{} is a non-directory; can't rmdir it", - path.display())) - }; - io_error::cond.raise(ioerr); - } - } - }, - None => { - let path = self.get_path(); - io_error::cond.raise(IoError { - kind: PathDoesntExist, - desc: "Path doesn't exist", - detail: Some(format!("{} doesn't exist; can't rmdir it", - path.display())) - }) - } - } - } - - // Get a collection of all entries at the given - // directory - fn readdir(&self) -> Option<~[Path]> { - readdir(self.get_path()) - } } -/// `DirectoryInfo` impl for `path::Path` -impl DirectoryInfo for Path { } - #[cfg(test)] mod test { - use super::super::{SeekSet, SeekCur, SeekEnd, - io_error, Read, Create, Open, ReadWrite}; - use super::super::super::test::*; + use path::{Path, GenericPath}; + use result::{Ok, Err}; use option::{Some, None}; - use path::Path; - use super::*; use iter::range; + use rt::test::run_in_mt_newsched_task; + use super::{open_stream, unlink, stat, copy, rmdir, mkdir, readdir, + open, create, rmdir_recursive, mkdir_recursive}; + + use rt::io; + use rt::io::Reader; + use super::super::{SeekSet, SeekCur, SeekEnd, + io_error, Read, Create, Open, ReadWrite}; + use vec::Vector; + + fn tmpdir() -> Path { + use os; + use rand; + let ret = os::tmpdir().join(format!("rust-{}", rand::random::<u32>())); + mkdir(&ret, io::UserRWX); + ret + } + #[test] fn file_test_io_smoke_test() { do run_in_mt_newsched_task { let message = "it's alright. have a good time"; let filename = &Path::new("./tmp/file_rt_io_file_test.txt"); { - let mut write_stream = open(filename, Create, ReadWrite).unwrap(); + let mut write_stream = open_stream(filename, Create, ReadWrite); write_stream.write(message.as_bytes()); } { use str; - let mut read_stream = open(filename, Open, Read).unwrap(); + let mut read_stream = open_stream(filename, Open, Read); let mut read_buf = [0, .. 1028]; let read_str = match read_stream.read(read_buf).unwrap() { -1|0 => fail!("shouldn't happen"), @@ -702,7 +628,7 @@ mod test { do io_error::cond.trap(|_| { called = true; }).inside { - let result = open(filename, Open, Read); + let result = open_stream(filename, Open, Read); assert!(result.is_none()); } assert!(called); @@ -731,11 +657,11 @@ mod test { let mut read_mem = [0, .. 8]; let filename = &Path::new("./tmp/file_rt_io_file_test_positional.txt"); { - let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); + let mut rw_stream = open_stream(filename, Create, ReadWrite); rw_stream.write(message.as_bytes()); } { - let mut read_stream = open(filename, Open, Read).unwrap(); + let mut read_stream = open_stream(filename, Open, Read); { let read_buf = read_mem.mut_slice(0, 4); read_stream.read(read_buf); @@ -762,11 +688,11 @@ mod test { let mut tell_pos_post_read; let filename = &Path::new("./tmp/file_rt_io_file_test_seeking.txt"); { - let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); + let mut rw_stream = open_stream(filename, Create, ReadWrite); rw_stream.write(message.as_bytes()); } { - let mut read_stream = open(filename, Open, Read).unwrap(); + let mut read_stream = open_stream(filename, Open, Read); read_stream.seek(set_cursor as i64, SeekSet); tell_pos_pre_read = read_stream.tell(); read_stream.read(read_mem); @@ -791,13 +717,13 @@ mod test { let mut read_mem = [0, .. 13]; let filename = &Path::new("./tmp/file_rt_io_file_test_seek_and_write.txt"); { - let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); + let mut rw_stream = open_stream(filename, Create, ReadWrite); rw_stream.write(initial_msg.as_bytes()); rw_stream.seek(seek_idx as i64, SeekSet); rw_stream.write(overwrite_msg.as_bytes()); } { - let mut read_stream = open(filename, Open, Read).unwrap(); + let mut read_stream = open_stream(filename, Open, Read); read_stream.read(read_mem); } unlink(filename); @@ -817,11 +743,11 @@ mod test { let mut read_mem = [0, .. 4]; let filename = &Path::new("./tmp/file_rt_io_file_test_seek_shakedown.txt"); { - let mut rw_stream = open(filename, Create, ReadWrite).unwrap(); + let mut rw_stream = open_stream(filename, Create, ReadWrite); rw_stream.write(initial_msg.as_bytes()); } { - let mut read_stream = open(filename, Open, Read).unwrap(); + let mut read_stream = open_stream(filename, Open, Read); read_stream.seek(-4, SeekEnd); read_stream.read(read_mem); @@ -847,14 +773,11 @@ mod test { do run_in_mt_newsched_task { let filename = &Path::new("./tmp/file_stat_correct_on_is_file.txt"); { - let mut fs = open(filename, Create, ReadWrite).unwrap(); + let mut fs = open_stream(filename, Create, ReadWrite); let msg = "hw"; fs.write(msg.as_bytes()); } - let stat_res = match stat(filename) { - Some(s) => s, - None => fail!("shouldn't happen") - }; + let stat_res = stat(filename); assert!(stat_res.is_file); unlink(filename); } @@ -864,11 +787,8 @@ mod test { fn file_test_stat_is_correct_on_is_dir() { do run_in_mt_newsched_task { let filename = &Path::new("./tmp/file_stat_correct_on_is_dir"); - mkdir(filename); - let stat_res = match stat(filename) { - Some(s) => s, - None => fail!("shouldn't happen") - }; + mkdir(filename, io::UserRWX); + let stat_res = filename.stat(); assert!(stat_res.is_dir); rmdir(filename); } @@ -878,7 +798,7 @@ mod test { fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { do run_in_mt_newsched_task { let dir = &Path::new("./tmp/fileinfo_false_on_dir"); - mkdir(dir); + mkdir(dir, io::UserRWX); assert!(dir.is_file() == false); rmdir(dir); } @@ -888,13 +808,9 @@ mod test { fn file_test_fileinfo_check_exists_before_and_after_file_creation() { do run_in_mt_newsched_task { let file = &Path::new("./tmp/fileinfo_check_exists_b_and_a.txt"); - { - let msg = "foo".as_bytes(); - let mut w = file.open_writer(Create); - w.write(msg); - } + create(file).write(bytes!("foo")); assert!(file.exists()); - file.unlink(); + unlink(file); assert!(!file.exists()); } } @@ -904,10 +820,10 @@ mod test { do run_in_mt_newsched_task { let dir = &Path::new("./tmp/before_and_after_dir"); assert!(!dir.exists()); - dir.mkdir(); + mkdir(dir, io::UserRWX); assert!(dir.exists()); assert!(dir.is_dir()); - dir.rmdir(); + rmdir(dir); assert!(!dir.exists()); } } @@ -917,36 +833,99 @@ mod test { use str; do run_in_mt_newsched_task { let dir = &Path::new("./tmp/di_readdir"); - dir.mkdir(); + mkdir(dir, io::UserRWX); let prefix = "foo"; for n in range(0,3) { let f = dir.join(format!("{}.txt", n)); - let mut w = f.open_writer(Create); + let mut w = create(&f); 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_str(); - let mut r = f.open_reader(Open); - r.read(mem); - let read_str = str::from_utf8(mem); - let expected = match n { - None|Some("") => fail!("really shouldn't happen.."), - Some(n) => prefix+n - }; - assert!(expected == read_str); - } - f.unlink(); - } - }, - None => fail!("shouldn't happen") + let files = readdir(dir); + let mut mem = [0u8, .. 4]; + for f in files.iter() { + { + let n = f.filestem_str(); + open(f).read(mem); + let read_str = str::from_utf8(mem); + let expected = match n { + None|Some("") => fail!("really shouldn't happen.."), + Some(n) => prefix+n + }; + assert!(expected == read_str); + } + unlink(f); } - dir.rmdir(); + rmdir(dir); } } + + #[test] + fn recursive_mkdir_slash() { + mkdir_recursive(&Path::new("/"), io::UserRWX); + } + + #[test] + fn unicode_path_is_dir() { + assert!(Path::new(".").is_dir()); + assert!(!Path::new("test/stdtest/fs.rs").is_dir()); + + let tmpdir = tmpdir(); + + let mut dirpath = tmpdir.clone(); + dirpath.push(format!("test-가一ー你好")); + mkdir(&dirpath, io::UserRWX); + assert!(dirpath.is_dir()); + + let mut filepath = dirpath; + filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs"); + create(&filepath); // ignore return; touch only + assert!(!filepath.is_dir()); + assert!(filepath.exists()); + + rmdir_recursive(&tmpdir); + } + + #[test] + fn unicode_path_exists() { + assert!(Path::new(".").exists()); + assert!(!Path::new("test/nonexistent-bogus-path").exists()); + + let tmpdir = tmpdir(); + let unicode = tmpdir.clone(); + let unicode = unicode.join(format!("test-각丁ー再见")); + mkdir(&unicode, io::UserRWX); + assert!(unicode.exists()); + assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); + rmdir_recursive(&tmpdir); + } + + #[test] + fn copy_file_does_not_exist() { + let from = Path::new("test/nonexistent-bogus-path"); + let to = Path::new("test/other-bogus-path"); + match io::result(|| copy(&from, &to)) { + Ok(*) => fail!(), + Err(*) => { + assert!(!from.exists()); + assert!(!to.exists()); + } + } + } + + #[test] + fn copy_file_ok() { + let tmpdir = tmpdir(); + let input = tmpdir.join("in.txt"); + let out = tmpdir.join("out.txt"); + + create(&input).write(bytes!("hello")); + copy(&input, &out); + let contents = open(&out).read_to_end(); + assert_eq!(contents.as_slice(), bytes!("hello")); + + assert_eq!(input.stat().mode, out.stat().mode); + rmdir_recursive(&tmpdir); + } } diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index be205749186..51204ca73b9 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -244,9 +244,12 @@ Out of scope use cast; use int; +use libc; use path::Path; -use prelude::*; use str::{StrSlice, OwnedStr}; +use option::{Option, Some, None}; +use result::{Ok, Err, Result}; +use iter::Iterator; use to_str::ToStr; use uint; use unstable::finally::Finally; @@ -418,6 +421,18 @@ pub fn ignore_io_error<T>(cb: &fn() -> T) -> T { } } +/// Helper for catching an I/O error and wrapping it in a Result object. The +/// return result will be the last I/O error that happened or the result of the +/// closure if no error occurred. +pub fn result<T>(cb: &fn() -> T) -> Result<T, IoError> { + let mut err = None; + let ret = io_error::cond.trap(|e| err = Some(e)).inside(cb); + match err { + Some(e) => Err(e), + None => Ok(ret), + } +} + pub trait Reader { // Only two methods which need to get implemented for this trait @@ -1137,3 +1152,7 @@ pub struct FileStat { /// platform-dependent msecs accessed: u64, } + +// FIXME(#10131): this needs to get designed for real +pub type FilePermission = u32; +pub static UserRWX: FilePermission = libc::S_IRWXU as FilePermission; diff --git a/src/libstd/rt/io/native/file.rs b/src/libstd/rt/io/native/file.rs index 9f9e7dcee9f..e26cc166c8d 100644 --- a/src/libstd/rt/io/native/file.rs +++ b/src/libstd/rt/io/native/file.rs @@ -297,3 +297,486 @@ mod tests { } } } + +// n.b. these functions were all part of the old `std::os` module. There's lots +// of fun little nuances that were taken care of by these functions, but +// they are all thread-blocking versions that are no longer desired (we now +// use a non-blocking event loop implementation backed by libuv). +// +// In theory we will have a thread-blocking version of the event loop (if +// desired), so these functions may just need to get adapted to work in +// those situtations. For now, I'm leaving the code around so it doesn't +// get bitrotted instantaneously. +mod old_os { + use prelude::*; + use c_str::CString; + use libc::fclose; + use libc::{size_t, c_void, c_int}; + use libc; + use vec; + + #[cfg(test)] use os; + #[cfg(test)] use rand; + + // On Windows, wide character version of function must be used to support + // unicode, so functions should be split into at least two versions, + // which are for Windows and for non-Windows, if necessary. + // See https://github.com/mozilla/rust/issues/9822 for more information. + + mod rustrt { + use libc::{c_char, c_int}; + use libc; + + extern { + pub fn rust_path_is_dir(path: *libc::c_char) -> c_int; + pub fn rust_path_exists(path: *libc::c_char) -> c_int; + } + + // Uses _wstat instead of stat. + #[cfg(windows)] + extern { + pub fn rust_path_is_dir_u16(path: *u16) -> c_int; + pub fn rust_path_exists_u16(path: *u16) -> c_int; + } + } + + /// Recursively walk a directory structure + pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool { + let r = list_dir(p); + r.iter().advance(|q| { + let path = &p.join(q); + f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p))) + }) + } + + #[cfg(unix)] + /// Indicates whether a path represents a directory + pub fn path_is_dir(p: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + do p.with_c_str |buf| { + rustrt::rust_path_is_dir(buf) != 0 as c_int + } + } + } + + + #[cfg(windows)] + pub fn path_is_dir(p: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| { + rustrt::rust_path_is_dir_u16(buf) != 0 as c_int + } + } + } + + #[cfg(unix)] + /// Indicates whether a path exists + pub fn path_exists(p: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + do p.with_c_str |buf| { + rustrt::rust_path_exists(buf) != 0 as c_int + } + } + } + + #[cfg(windows)] + pub fn path_exists(p: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + do os::win32::as_utf16_p(p.as_str().unwrap()) |buf| { + rustrt::rust_path_exists_u16(buf) != 0 as c_int + } + } + } + + /// Creates a directory at the specified path + pub fn make_dir(p: &Path, mode: c_int) -> bool { + return mkdir(p, mode); + + #[cfg(windows)] + fn mkdir(p: &Path, _mode: c_int) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + use os::win32::as_utf16_p; + // FIXME: turn mode into something useful? #2623 + do as_utf16_p(p.as_str().unwrap()) |buf| { + libc::CreateDirectoryW(buf, ptr::mut_null()) + != (0 as libc::BOOL) + } + } + } + + #[cfg(unix)] + fn mkdir(p: &Path, mode: c_int) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + do p.with_c_str |buf| { + unsafe { + libc::mkdir(buf, mode as libc::mode_t) == (0 as c_int) + } + } + } + } + + /// Creates a directory with a given mode. + /// Returns true iff creation + /// succeeded. Also creates all intermediate subdirectories + /// if they don't already exist, giving all of them the same mode. + + // tjc: if directory exists but with different permissions, + // should we return false? + pub fn mkdir_recursive(p: &Path, mode: c_int) -> bool { + if path_is_dir(p) { + return true; + } + if p.filename().is_some() { + let mut p_ = p.clone(); + p_.pop(); + if !mkdir_recursive(&p_, mode) { + return false; + } + } + return make_dir(p, mode); + } + + /// Lists the contents of a directory + /// + /// Each resulting Path is a relative path with no directory component. + pub fn list_dir(p: &Path) -> ~[Path] { + unsafe { + #[cfg(target_os = "linux")] + #[cfg(target_os = "android")] + #[cfg(target_os = "freebsd")] + #[cfg(target_os = "macos")] + unsafe fn get_list(p: &Path) -> ~[Path] { + #[fixed_stack_segment]; #[inline(never)]; + use libc::{dirent_t}; + use libc::{opendir, readdir, closedir}; + extern { + fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; + } + let mut paths = ~[]; + debug!("os::list_dir -- BEFORE OPENDIR"); + + let dir_ptr = do p.with_c_str |buf| { + opendir(buf) + }; + + if (dir_ptr as uint != 0) { + debug!("os::list_dir -- opendir() SUCCESS"); + let mut entry_ptr = readdir(dir_ptr); + while (entry_ptr as uint != 0) { + let cstr = CString::new(rust_list_dir_val(entry_ptr), false); + paths.push(Path::new(cstr)); + entry_ptr = readdir(dir_ptr); + } + closedir(dir_ptr); + } + else { + debug!("os::list_dir -- opendir() FAILURE"); + } + debug!("os::list_dir -- AFTER -- \\#: {}", paths.len()); + paths + } + #[cfg(windows)] + unsafe fn get_list(p: &Path) -> ~[Path] { + #[fixed_stack_segment]; #[inline(never)]; + use libc::consts::os::extra::INVALID_HANDLE_VALUE; + use libc::{wcslen, free}; + use libc::funcs::extra::kernel32::{ + FindFirstFileW, + FindNextFileW, + FindClose, + }; + use libc::types::os::arch::extra::HANDLE; + use os::win32::{ + as_utf16_p + }; + use rt::global_heap::malloc_raw; + + #[nolink] + extern { + fn rust_list_dir_wfd_size() -> libc::size_t; + fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16; + } + let star = p.join("*"); + do as_utf16_p(star.as_str().unwrap()) |path_ptr| { + let mut paths = ~[]; + let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); + let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE); + if find_handle as libc::c_int != INVALID_HANDLE_VALUE { + let mut more_files = 1 as libc::c_int; + while more_files != 0 { + let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr); + if fp_buf as uint == 0 { + fail!("os::list_dir() failure: got null ptr from wfd"); + } + else { + let fp_vec = vec::from_buf( + fp_buf, wcslen(fp_buf) as uint); + let fp_str = str::from_utf16(fp_vec); + paths.push(Path::new(fp_str)); + } + more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); + } + FindClose(find_handle); + free(wfd_ptr) + } + paths + } + } + do get_list(p).move_iter().filter |path| { + path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..") + }.collect() + } + } + + /// Removes a directory at the specified path, after removing + /// all its contents. Use carefully! + pub fn remove_dir_recursive(p: &Path) -> bool { + let mut error_happened = false; + do walk_dir(p) |inner| { + if !error_happened { + if path_is_dir(inner) { + if !remove_dir_recursive(inner) { + error_happened = true; + } + } + else { + if !remove_file(inner) { + error_happened = true; + } + } + } + true + }; + // Directory should now be empty + !error_happened && remove_dir(p) + } + + /// Removes a directory at the specified path + pub fn remove_dir(p: &Path) -> bool { + return rmdir(p); + + #[cfg(windows)] + fn rmdir(p: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + use os::win32::as_utf16_p; + return do as_utf16_p(p.as_str().unwrap()) |buf| { + libc::RemoveDirectoryW(buf) != (0 as libc::BOOL) + }; + } + } + + #[cfg(unix)] + fn rmdir(p: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + do p.with_c_str |buf| { + unsafe { + libc::rmdir(buf) == (0 as c_int) + } + } + } + } + + /// Deletes an existing file + pub fn remove_file(p: &Path) -> bool { + return unlink(p); + + #[cfg(windows)] + fn unlink(p: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + use os::win32::as_utf16_p; + return do as_utf16_p(p.as_str().unwrap()) |buf| { + libc::DeleteFileW(buf) != (0 as libc::BOOL) + }; + } + } + + #[cfg(unix)] + fn unlink(p: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + do p.with_c_str |buf| { + libc::unlink(buf) == (0 as c_int) + } + } + } + } + + /// Renames an existing file or directory + pub fn rename_file(old: &Path, new: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + do old.with_c_str |old_buf| { + do new.with_c_str |new_buf| { + libc::rename(old_buf, new_buf) == (0 as c_int) + } + } + } + } + + /// Copies a file from one location to another + pub fn copy_file(from: &Path, to: &Path) -> bool { + return do_copy_file(from, to); + + #[cfg(windows)] + fn do_copy_file(from: &Path, to: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + use os::win32::as_utf16_p; + return do as_utf16_p(from.as_str().unwrap()) |fromp| { + do as_utf16_p(to.as_str().unwrap()) |top| { + libc::CopyFileW(fromp, top, (0 as libc::BOOL)) != + (0 as libc::BOOL) + } + } + } + } + + #[cfg(unix)] + fn do_copy_file(from: &Path, to: &Path) -> bool { + #[fixed_stack_segment]; #[inline(never)]; + unsafe { + let istream = do from.with_c_str |fromp| { + do "rb".with_c_str |modebuf| { + libc::fopen(fromp, modebuf) + } + }; + if istream as uint == 0u { + return false; + } + // Preserve permissions + let from_mode = from.stat().mode; + + let ostream = do to.with_c_str |top| { + do "w+b".with_c_str |modebuf| { + libc::fopen(top, modebuf) + } + }; + if ostream as uint == 0u { + fclose(istream); + return false; + } + let bufsize = 8192u; + let mut buf = vec::with_capacity::<u8>(bufsize); + let mut done = false; + let mut ok = true; + while !done { + do buf.as_mut_buf |b, _sz| { + let nread = libc::fread(b as *mut c_void, 1u as size_t, + bufsize as size_t, + istream); + if nread > 0 as size_t { + if libc::fwrite(b as *c_void, 1u as size_t, nread, + ostream) != nread { + ok = false; + done = true; + } + } else { + done = true; + } + } + } + fclose(istream); + fclose(ostream); + + // Give the new file the old file's permissions + if do to.with_c_str |to_buf| { + libc::chmod(to_buf, from_mode as libc::mode_t) + } != 0 { + return false; // should be a condition... + } + return ok; + } + } + } + + #[test] + fn tmpdir() { + let p = os::tmpdir(); + let s = p.as_str(); + assert!(s.is_some() && s.unwrap() != "."); + } + + // Issue #712 + #[test] + fn test_list_dir_no_invalid_memory_access() { + list_dir(&Path::new(".")); + } + + #[test] + fn test_list_dir() { + let dirs = list_dir(&Path::new(".")); + // Just assuming that we've got some contents in the current directory + assert!(dirs.len() > 0u); + + for dir in dirs.iter() { + debug!("{:?}", (*dir).clone()); + } + } + + #[test] + #[cfg(not(windows))] + fn test_list_dir_root() { + let dirs = list_dir(&Path::new("/")); + assert!(dirs.len() > 1); + } + #[test] + #[cfg(windows)] + fn test_list_dir_root() { + let dirs = list_dir(&Path::new("C:\\")); + assert!(dirs.len() > 1); + } + + #[test] + fn test_path_is_dir() { + use rt::io::file::{open_stream, mkdir_recursive}; + use rt::io::{OpenOrCreate, Read, UserRWX}; + + assert!((path_is_dir(&Path::new(".")))); + assert!((!path_is_dir(&Path::new("test/stdtest/fs.rs")))); + + let mut dirpath = os::tmpdir(); + dirpath.push(format!("rust-test-{}/test-\uac00\u4e00\u30fc\u4f60\u597d", + rand::random::<u32>())); // 가一ー你好 + debug!("path_is_dir dirpath: {}", dirpath.display()); + + mkdir_recursive(&dirpath, UserRWX); + + assert!((path_is_dir(&dirpath))); + + let mut filepath = dirpath; + filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs"); + debug!("path_is_dir filepath: {}", filepath.display()); + + open_stream(&filepath, OpenOrCreate, Read); // ignore return; touch only + assert!((!path_is_dir(&filepath))); + + assert!((!path_is_dir(&Path::new( + "test/unicode-bogus-dir-\uac00\u4e00\u30fc\u4f60\u597d")))); + } + + #[test] + fn test_path_exists() { + use rt::io::file::mkdir_recursive; + use rt::io::UserRWX; + + assert!((path_exists(&Path::new(".")))); + assert!((!path_exists(&Path::new( + "test/nonexistent-bogus-path")))); + + let mut dirpath = os::tmpdir(); + dirpath.push(format!("rust-test-{}/test-\uac01\u4e01\u30fc\u518d\u89c1", + rand::random::<u32>())); // 각丁ー再见 + + mkdir_recursive(&dirpath, UserRWX); + assert!((path_exists(&dirpath))); + assert!((!path_exists(&Path::new( + "test/unicode-bogus-path-\uac01\u4e01\u30fc\u518d\u89c1")))); + } +} diff --git a/src/libstd/rt/io/net/unix.rs b/src/libstd/rt/io/net/unix.rs index f30423812ba..dd8a999c6de 100644 --- a/src/libstd/rt/io/net/unix.rs +++ b/src/libstd/rt/io/net/unix.rs @@ -156,7 +156,6 @@ mod tests { use rt::test::*; use rt::io::*; use rt::comm::oneshot; - use os; fn smalltest(server: ~fn(UnixStream), client: ~fn(UnixStream)) { let server = Cell::new(server); @@ -290,7 +289,7 @@ mod tests { do run_in_mt_newsched_task { let path = next_test_unix(); let _acceptor = UnixListener::bind(&path).listen(); - assert!(os::path_exists(&path)); + assert!(path.exists()); } } } diff --git a/src/libstd/rt/io/signal.rs b/src/libstd/rt/io/signal.rs index b782d713950..1b856ab0755 100644 --- a/src/libstd/rt/io/signal.rs +++ b/src/libstd/rt/io/signal.rs @@ -19,6 +19,7 @@ definitions for a number of signals. */ +use container::{Map, MutableMap}; use comm::{Port, SharedChan, stream}; use hashmap; use option::{Some, None}; @@ -146,10 +147,10 @@ impl Listener { #[cfg(test)] mod test { - use super::*; - use libc; use rt::io::timer; + use super::{Listener, Interrupt}; + use comm::{GenericPort, Peekable}; // kill is only available on Unixes #[cfg(unix)] @@ -207,7 +208,7 @@ mod test { #[cfg(windows)] #[test] fn test_io_signal_invalid_signum() { - use rt::io; + use super::User1; let mut s = Listener::new(); let mut called = false; do io::io_error::cond.trap(|_| { diff --git a/src/libstd/rt/io/timer.rs b/src/libstd/rt/io/timer.rs index 500cd91b3db..36092dfbe34 100644 --- a/src/libstd/rt/io/timer.rs +++ b/src/libstd/rt/io/timer.rs @@ -108,6 +108,7 @@ impl Timer { #[cfg(test)] mod test { + use prelude::*; use super::*; use rt::test::*; use cell::Cell; diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index 44d9f59c410..a0db1f1df00 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -22,7 +22,7 @@ use super::io::process::ProcessConfig; use super::io::net::ip::{IpAddr, SocketAddr}; use path::Path; use super::io::{SeekStyle}; -use super::io::{FileMode, FileAccess, FileStat}; +use super::io::{FileMode, FileAccess, FileStat, FilePermission}; pub trait EventLoop { fn run(&mut self); @@ -102,7 +102,10 @@ pub trait IoFactory { -> Result<~RtioFileStream, IoError>; fn fs_unlink(&mut self, path: &CString) -> Result<(), IoError>; fn fs_stat(&mut self, path: &CString) -> Result<FileStat, IoError>; - fn fs_mkdir(&mut self, path: &CString, mode: int) -> Result<(), IoError>; + fn fs_mkdir(&mut self, path: &CString, + mode: FilePermission) -> Result<(), IoError>; + fn fs_chmod(&mut self, path: &CString, + mode: FilePermission) -> Result<(), IoError>; fn fs_rmdir(&mut self, path: &CString) -> Result<(), IoError>; fn fs_rename(&mut self, path: &CString, to: &CString) -> Result<(), IoError>; fn fs_readdir(&mut self, path: &CString, flags: c_int) -> |
