diff options
| author | Fenrir <[email protected]> | 2018-01-21 14:06:28 -0700 |
|---|---|---|
| committer | FenrirWolf <[email protected]> | 2018-01-21 19:16:33 -0700 |
| commit | 23be3f4885688e5e0011005e2295c75168854c0a (patch) | |
| tree | dd0850f9c73c489e114a761d5c0757f3dbec3a65 /ctr-std/src/fs.rs | |
| parent | Update CI for Rust nightly-2017-12-01 + other fixes (diff) | |
| download | archived-ctru-rs-23be3f4885688e5e0011005e2295c75168854c0a.tar.xz archived-ctru-rs-23be3f4885688e5e0011005e2295c75168854c0a.zip | |
Recreate ctr-std from latest nightly
Diffstat (limited to 'ctr-std/src/fs.rs')
| -rw-r--r-- | ctr-std/src/fs.rs | 612 |
1 files changed, 487 insertions, 125 deletions
diff --git a/ctr-std/src/fs.rs b/ctr-std/src/fs.rs index e91e808..d1f3ccb 100644 --- a/ctr-std/src/fs.rs +++ b/ctr-std/src/fs.rs @@ -19,7 +19,7 @@ use fmt; use ffi::OsString; -use io::{self, SeekFrom, Seek, Read, Write}; +use io::{self, SeekFrom, Seek, Read, Initializer, Write}; use path::{Path, PathBuf}; use sys::fs as fs_imp; use sys_common::{AsInnerMut, FromInner, AsInner, IntoInner}; @@ -28,28 +28,63 @@ use time::SystemTime; /// A reference to an open file on the filesystem. /// /// An instance of a `File` can be read and/or written depending on what options -/// it was opened with. Files also implement `Seek` to alter the logical cursor +/// it was opened with. Files also implement [`Seek`] to alter the logical cursor /// that the file contains internally. /// /// Files are automatically closed when they go out of scope. /// /// # Examples /// +/// Create a new file and write bytes to it: +/// /// ```no_run +/// use std::fs::File; /// use std::io::prelude::*; +/// +/// # fn foo() -> std::io::Result<()> { +/// let mut file = File::create("foo.txt")?; +/// file.write_all(b"Hello, world!")?; +/// # Ok(()) +/// # } +/// ``` +/// +/// Read the contents of a file into a [`String`]: +/// +/// ```no_run /// use std::fs::File; +/// use std::io::prelude::*; /// /// # fn foo() -> std::io::Result<()> { -/// let mut f = try!(File::create("foo.txt")); -/// try!(f.write_all(b"Hello, world!")); +/// let mut file = File::open("foo.txt")?; +/// let mut contents = String::new(); +/// file.read_to_string(&mut contents)?; +/// assert_eq!(contents, "Hello, world!"); +/// # Ok(()) +/// # } +/// ``` +/// +/// It can be more efficient to read the contents of a file with a buffered +/// [`Read`]er. This can be accomplished with [`BufReader<R>`]: /// -/// let mut f = try!(File::open("foo.txt")); -/// let mut s = String::new(); -/// try!(f.read_to_string(&mut s)); -/// assert_eq!(s, "Hello, world!"); +/// ```no_run +/// use std::fs::File; +/// use std::io::BufReader; +/// use std::io::prelude::*; +/// +/// # fn foo() -> std::io::Result<()> { +/// let file = File::open("foo.txt")?; +/// let mut buf_reader = BufReader::new(file); +/// let mut contents = String::new(); +/// buf_reader.read_to_string(&mut contents)?; +/// assert_eq!(contents, "Hello, world!"); /// # Ok(()) /// # } /// ``` +/// +/// [`Seek`]: ../io/trait.Seek.html +/// [`String`]: ../string/struct.String.html +/// [`Read`]: ../io/trait.Read.html +/// [`BufReader<R>`]: ../io/struct.BufReader.html #[stable(feature = "rust1", since = "1.0.0")] pub struct File { inner: fs_imp::File, @@ -57,11 +92,13 @@ pub struct File { /// Metadata information about a file. /// -/// This structure is returned from the [`metadata`] function or method and -/// represents known metadata about a file such as its permissions, size, -/// modification times, etc. +/// This structure is returned from the [`metadata`] or +/// [`symlink_metadata`] function or method and represents known +/// metadata about a file such as its permissions, size, modification +/// times, etc. /// /// [`metadata`]: fn.metadata.html +/// [`symlink_metadata`]: fn.symlink_metadata.html #[stable(feature = "rust1", since = "1.0.0")] #[derive(Clone)] pub struct Metadata(fs_imp::FileAttr); @@ -69,19 +106,19 @@ pub struct Metadata(fs_imp::FileAttr); /// Iterator over the entries in a directory. /// /// This iterator is returned from the [`read_dir`] function of this module and -/// will yield instances of `io::Result<DirEntry>`. Through a [`DirEntry`] +/// will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. Through a [`DirEntry`] /// information like the entry's path and possibly other metadata can be /// learned. /// -/// [`read_dir`]: fn.read_dir.html -/// [`DirEntry`]: struct.DirEntry.html -/// /// # Errors /// -/// This [`io::Result`] will be an `Err` if there's some sort of intermittent +/// This [`io::Result`] will be an [`Err`] if there's some sort of intermittent /// IO error during iteration. /// +/// [`read_dir`]: fn.read_dir.html +/// [`DirEntry`]: struct.DirEntry.html /// [`io::Result`]: ../io/type.Result.html +/// [`Err`]: ../result/enum.Result.html#variant.Err #[stable(feature = "rust1", since = "1.0.0")] #[derive(Debug)] pub struct ReadDir(fs_imp::ReadDir); @@ -107,14 +144,14 @@ pub struct DirEntry(fs_imp::DirEntry); /// [`File::open`]: struct.File.html#method.open /// [`File::create`]: struct.File.html#method.create /// -/// Generally speaking, when using `OpenOptions`, you'll first call [`new()`], -/// then chain calls to methods to set each option, then call [`open()`], +/// Generally speaking, when using `OpenOptions`, you'll first call [`new`], +/// then chain calls to methods to set each option, then call [`open`], /// passing the path of the file you're trying to open. This will give you a /// [`io::Result`][result] with a [`File`][file] inside that you can further /// operate on. /// -/// [`new()`]: struct.OpenOptions.html#method.new -/// [`open()`]: struct.OpenOptions.html#method.open +/// [`new`]: struct.OpenOptions.html#method.new +/// [`open`]: struct.OpenOptions.html#method.open /// [result]: ../io/type.Result.html /// [file]: struct.File.html /// @@ -140,7 +177,7 @@ pub struct DirEntry(fs_imp::DirEntry); /// .create(true) /// .open("foo.txt"); /// ``` -#[derive(Clone)] +#[derive(Clone, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct OpenOptions(fs_imp::OpenOptions); @@ -168,11 +205,131 @@ pub struct FileType(fs_imp::FileType); /// /// This builder also supports platform-specific options. #[stable(feature = "dir_builder", since = "1.6.0")] +#[derive(Debug)] pub struct DirBuilder { inner: fs_imp::DirBuilder, recursive: bool, } +/// How large a buffer to pre-allocate before reading the entire file. +fn initial_buffer_size(file: &File) -> usize { + // Allocate one extra byte so the buffer doesn't need to grow before the + // final `read` call at the end of the file. Don't worry about `usize` + // overflow because reading will fail regardless in that case. + file.metadata().map(|m| m.len() as usize + 1).unwrap_or(0) +} + +/// Read the entire contents of a file into a bytes vector. +/// +/// This is a convenience function for using [`File::open`] and [`read_to_end`] +/// with fewer imports and without an intermediate variable. +/// +/// [`File::open`]: struct.File.html#method.open +/// [`read_to_end`]: ../io/trait.Read.html#method.read_to_end +/// +/// # Errors +/// +/// This function will return an error if `path` does not already exist. +/// Other errors may also be returned according to [`OpenOptions::open`]. +/// +/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open +/// +/// It will also return an error if it encounters while reading an error +/// of a kind other than [`ErrorKind::Interrupted`]. +/// +/// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted +/// +/// # Examples +/// +/// ```no_run +/// #![feature(fs_read_write)] +/// +/// use std::fs; +/// use std::net::SocketAddr; +/// +/// # fn foo() -> Result<(), Box<std::error::Error + 'static>> { +/// let foo: SocketAddr = String::from_utf8_lossy(&fs::read("address.txt")?).parse()?; +/// # Ok(()) +/// # } +/// ``` +#[unstable(feature = "fs_read_write", issue = "46588")] +pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> { + let mut file = File::open(path)?; + let mut bytes = Vec::with_capacity(initial_buffer_size(&file)); + file.read_to_end(&mut bytes)?; + Ok(bytes) +} + +/// Read the entire contents of a file into a string. +/// +/// This is a convenience function for using [`File::open`] and [`read_to_string`] +/// with fewer imports and without an intermediate variable. +/// +/// [`File::open`]: struct.File.html#method.open +/// [`read_to_string`]: ../io/trait.Read.html#method.read_to_string +/// +/// # Errors +/// +/// This function will return an error if `path` does not already exist. +/// Other errors may also be returned according to [`OpenOptions::open`]. +/// +/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open +/// +/// It will also return an error if it encounters while reading an error +/// of a kind other than [`ErrorKind::Interrupted`], +/// or if the contents of the file are not valid UTF-8. +/// +/// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted +/// +/// # Examples +/// +/// ```no_run +/// #![feature(fs_read_write)] +/// +/// use std::fs; +/// use std::net::SocketAddr; +/// +/// # fn foo() -> Result<(), Box<std::error::Error + 'static>> { +/// let foo: SocketAddr = fs::read_string("address.txt")?.parse()?; +/// # Ok(()) +/// # } +/// ``` +#[unstable(feature = "fs_read_write", issue = "46588")] +pub fn read_string<P: AsRef<Path>>(path: P) -> io::Result<String> { + let mut file = File::open(path)?; + let mut string = String::with_capacity(initial_buffer_size(&file)); + file.read_to_string(&mut string)?; + Ok(string) +} + +/// Write a slice as the entire contents of a file. +/// +/// This function will create a file if it does not exist, +/// and will entirely replace its contents if it does. +/// +/// This is a convenience function for using [`File::create`] and [`write_all`] +/// with fewer imports. +/// +/// [`File::create`]: struct.File.html#method.create +/// [`write_all`]: ../io/trait.Write.html#method.write_all +/// +/// # Examples +/// +/// ```no_run +/// #![feature(fs_read_write)] +/// +/// use std::fs; +/// +/// # fn foo() -> std::io::Result<()> { +/// fs::write("foo.txt", b"Lorem ipsum")?; +/// # Ok(()) +/// # } +/// ``` +#[unstable(feature = "fs_read_write", issue = "46588")] +pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { + File::create(path)?.write_all(contents.as_ref()) +} + impl File { /// Attempts to open a file in read-only mode. /// @@ -191,7 +348,7 @@ impl File { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::open("foo.txt")); + /// let mut f = File::open("foo.txt")?; /// # Ok(()) /// # } /// ``` @@ -215,7 +372,7 @@ impl File { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::create("foo.txt")); + /// let mut f = File::create("foo.txt")?; /// # Ok(()) /// # } /// ``` @@ -236,10 +393,10 @@ impl File { /// use std::io::prelude::*; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::create("foo.txt")); - /// try!(f.write_all(b"Hello, world!")); + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello, world!")?; /// - /// try!(f.sync_all()); + /// f.sync_all()?; /// # Ok(()) /// # } /// ``` @@ -267,10 +424,10 @@ impl File { /// use std::io::prelude::*; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::create("foo.txt")); - /// try!(f.write_all(b"Hello, world!")); + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello, world!")?; /// - /// try!(f.sync_data()); + /// f.sync_data()?; /// # Ok(()) /// # } /// ``` @@ -297,8 +454,8 @@ impl File { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::create("foo.txt")); - /// try!(f.set_len(10)); + /// let mut f = File::create("foo.txt")?; + /// f.set_len(10)?; /// # Ok(()) /// # } /// ``` @@ -315,8 +472,8 @@ impl File { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::open("foo.txt")); - /// let metadata = try!(f.metadata()); + /// let mut f = File::open("foo.txt")?; + /// let metadata = f.metadata()?; /// # Ok(()) /// # } /// ``` @@ -337,8 +494,8 @@ impl File { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::open("foo.txt")); - /// let file_copy = try!(f.try_clone()); + /// let mut f = File::open("foo.txt")?; + /// let file_copy = f.try_clone()?; /// # Ok(()) /// # } /// ``` @@ -368,7 +525,6 @@ impl File { /// # Examples /// /// ``` - /// #![feature(set_permissions_atomic)] /// # fn foo() -> std::io::Result<()> { /// use std::fs::File; /// @@ -379,7 +535,7 @@ impl File { /// # Ok(()) /// # } /// ``` - #[unstable(feature = "set_permissions_atomic", issue="37916")] + #[stable(feature = "set_permissions_atomic", since = "1.16.0")] pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> { self.inner.set_permissions(perm.0) } @@ -411,8 +567,10 @@ impl Read for File { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.inner.read(buf) } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { - self.inner.read_to_end(buf) + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() } } #[stable(feature = "rust1", since = "1.0.0")] @@ -433,8 +591,10 @@ impl<'a> Read for &'a File { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.inner.read(buf) } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { - self.inner.read_to_end(buf) + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() } } #[stable(feature = "rust1", since = "1.0.0")] @@ -519,14 +679,26 @@ impl OpenOptions { /// /// One maybe obvious note when using append-mode: make sure that all data /// that belongs together is written to the file in one operation. This - /// can be done by concatenating strings before passing them to `write()`, + /// can be done by concatenating strings before passing them to [`write()`], /// or using a buffered writer (with a buffer of adequate size), - /// and calling `flush()` when the message is complete. + /// and calling [`flush()`] when the message is complete. /// /// If a file is opened with both read and append access, beware that after /// opening, and after every write, the position for reading may be set at the /// end of the file. So, before writing, save the current position (using - /// `seek(SeekFrom::Current(0))`, and restore it before the next read. + /// [`seek`]`(`[`SeekFrom`]`::`[`Current`]`(0))`, and restore it before the next read. + /// + /// ## Note + /// + /// This function doesn't create the file if it doesn't exist. Use the [`create`] + /// method to do so. + /// + /// [`write()`]: ../../std/fs/struct.File.html#method.write + /// [`flush()`]: ../../std/fs/struct.File.html#method.flush + /// [`seek`]: ../../std/fs/struct.File.html#method.seek + /// [`SeekFrom`]: ../../std/io/enum.SeekFrom.html + /// [`Current`]: ../../std/io/enum.SeekFrom.html#variant.Current + /// [`create`]: #method.create /// /// # Examples /// @@ -564,9 +736,12 @@ impl OpenOptions { /// This option indicates whether a new file will be created if the file /// does not yet already exist. /// - /// In order for the file to be created, `write` or `append` access must + /// In order for the file to be created, [`write`] or [`append`] access must /// be used. /// + /// [`write`]: #method.write + /// [`append`]: #method.append + /// /// # Examples /// /// ```no_run @@ -589,12 +764,15 @@ impl OpenOptions { /// whether a file exists and creating a new one, the file may have been /// created by another process (a TOCTOU race condition / attack). /// - /// If `.create_new(true)` is set, `.create()` and `.truncate()` are + /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are /// ignored. /// /// The file must be opened with write or append access in order to create /// a new file. /// + /// [`.create()`]: #method.create + /// [`.truncate()`]: #method.truncate + /// /// # Examples /// /// ```no_run @@ -614,15 +792,29 @@ impl OpenOptions { /// # Errors /// /// This function will return an error under a number of different - /// circumstances, to include but not limited to: - /// - /// * Opening a file that does not exist without setting `create` or - /// `create_new`. - /// * Attempting to open a file with access that the user lacks - /// permissions for - /// * Filesystem-level errors (full disk, etc) - /// * Invalid combinations of open options (truncate without write access, - /// no access mode set, etc) + /// circumstances. Some of these error conditions are listed here, together + /// with their [`ErrorKind`]. The mapping to [`ErrorKind`]s is not part of + /// the compatibility contract of the function, especially the `Other` kind + /// might change to more specific kinds in the future. + /// + /// * [`NotFound`]: The specified file does not exist and neither `create` + /// or `create_new` is set. + /// * [`NotFound`]: One of the directory components of the file path does + /// not exist. + /// * [`PermissionDenied`]: The user lacks permission to get the specified + /// access rights for the file. + /// * [`PermissionDenied`]: The user lacks permission to open one of the + /// directory components of the specified path. + /// * [`AlreadyExists`]: `create_new` was specified and the file already + /// exists. + /// * [`InvalidInput`]: Invalid combinations of open options (truncate + /// without write access, no access mode set, etc.). + /// * [`Other`]: One of the directory components of the specified file path + /// was not, in fact, a directory. + /// * [`Other`]: Filesystem-level errors: full disk, write permission + /// requested on a read-only file system, exceeded disk quota, too many + /// open files, too long filename, too many symbolic links in the + /// specified path (Unix-like systems only), etc. /// /// # Examples /// @@ -631,6 +823,13 @@ impl OpenOptions { /// /// let file = OpenOptions::new().open("foo.txt"); /// ``` + /// + /// [`ErrorKind`]: ../io/enum.ErrorKind.html + /// [`AlreadyExists`]: ../io/enum.ErrorKind.html#variant.AlreadyExists + /// [`InvalidInput`]: ../io/enum.ErrorKind.html#variant.InvalidInput + /// [`NotFound`]: ../io/enum.ErrorKind.html#variant.NotFound + /// [`Other`]: ../io/enum.ErrorKind.html#variant.Other + /// [`PermissionDenied`]: ../io/enum.ErrorKind.html#variant.PermissionDenied #[stable(feature = "rust1", since = "1.0.0")] pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> { self._open(path.as_ref()) @@ -655,7 +854,7 @@ impl Metadata { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// /// println!("{:?}", metadata.file_type()); /// # Ok(()) @@ -674,7 +873,7 @@ impl Metadata { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// /// assert!(!metadata.is_dir()); /// # Ok(()) @@ -691,7 +890,7 @@ impl Metadata { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// /// assert!(metadata.is_file()); /// # Ok(()) @@ -708,7 +907,7 @@ impl Metadata { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// /// assert_eq!(0, metadata.len()); /// # Ok(()) @@ -725,7 +924,7 @@ impl Metadata { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// /// assert!(!metadata.permissions().readonly()); /// # Ok(()) @@ -752,7 +951,7 @@ impl Metadata { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// /// if let Ok(time) = metadata.modified() { /// println!("{:?}", time); @@ -787,7 +986,7 @@ impl Metadata { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// /// if let Ok(time) = metadata.accessed() { /// println!("{:?}", time); @@ -818,7 +1017,7 @@ impl Metadata { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// /// if let Ok(time) = metadata.created() { /// println!("{:?}", time); @@ -834,12 +1033,27 @@ impl Metadata { } } +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for Metadata { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Metadata") + .field("file_type", &self.file_type()) + .field("is_dir", &self.is_dir()) + .field("is_file", &self.is_file()) + .field("permissions", &self.permissions()) + .field("modified", &self.modified()) + .field("accessed", &self.accessed()) + .field("created", &self.created()) + .finish() + } +} + impl AsInner<fs_imp::FileAttr> for Metadata { fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 } } impl Permissions { - /// Returns whether these permissions describe a readonly file. + /// Returns whether these permissions describe a readonly (unwritable) file. /// /// # Examples /// @@ -847,8 +1061,8 @@ impl Permissions { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::create("foo.txt")); - /// let metadata = try!(f.metadata()); + /// let mut f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; /// /// assert_eq!(false, metadata.permissions().readonly()); /// # Ok(()) @@ -857,7 +1071,11 @@ impl Permissions { #[stable(feature = "rust1", since = "1.0.0")] pub fn readonly(&self) -> bool { self.0.readonly() } - /// Modifies the readonly flag for this set of permissions. + /// Modifies the readonly flag for this set of permissions. If the + /// `readonly` argument is `true`, using the resulting `Permission` will + /// update file permissions to forbid writing. Conversely, if it's `false`, + /// using the resulting `Permission` will update file permissions to allow + /// writing. /// /// This operation does **not** modify the filesystem. To modify the /// filesystem use the `fs::set_permissions` function. @@ -868,8 +1086,8 @@ impl Permissions { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let f = try!(File::create("foo.txt")); - /// let metadata = try!(f.metadata()); + /// let f = File::create("foo.txt")?; + /// let metadata = f.metadata()?; /// let mut permissions = metadata.permissions(); /// /// permissions.set_readonly(true); @@ -897,7 +1115,7 @@ impl FileType { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// let file_type = metadata.file_type(); /// /// assert_eq!(file_type.is_dir(), false); @@ -915,7 +1133,7 @@ impl FileType { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::metadata("foo.txt")?; /// let file_type = metadata.file_type(); /// /// assert_eq!(file_type.is_file(), true); @@ -927,13 +1145,24 @@ impl FileType { /// Test whether this file type represents a symbolic link. /// + /// The underlying [`Metadata`] struct needs to be retrieved + /// with the [`fs::symlink_metadata`] function and not the + /// [`fs::metadata`] function. The [`fs::metadata`] function + /// follows symbolic links, so [`is_symlink`] would always + /// return false for the target file. + /// + /// [`Metadata`]: struct.Metadata.html + /// [`fs::metadata`]: fn.metadata.html + /// [`fs::symlink_metadata`]: fn.symlink_metadata.html + /// [`is_symlink`]: struct.FileType.html#method.is_symlink + /// /// # Examples /// /// ``` /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// - /// let metadata = try!(fs::metadata("foo.txt")); + /// let metadata = fs::symlink_metadata("foo.txt")?; /// let file_type = metadata.file_type(); /// /// assert_eq!(file_type.is_symlink(), false); @@ -978,8 +1207,8 @@ impl DirEntry { /// ``` /// use std::fs; /// # fn foo() -> std::io::Result<()> { - /// for entry in try!(fs::read_dir(".")) { - /// let dir = try!(entry); + /// for entry in fs::read_dir(".")? { + /// let dir = entry?; /// println!("{:?}", dir.path()); /// } /// # Ok(()) @@ -1115,6 +1344,7 @@ impl AsInner<fs_imp::DirEntry> for DirEntry { /// This function currently corresponds to the `unlink` function on Unix /// and the `DeleteFile` function on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1131,7 +1361,7 @@ impl AsInner<fs_imp::DirEntry> for DirEntry { /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::remove_file("a.txt")); +/// fs::remove_file("a.txt")?; /// # Ok(()) /// # } /// ``` @@ -1151,6 +1381,7 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> { /// This function currently corresponds to the `stat` function on Unix /// and the `GetFileAttributesEx` function on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1167,7 +1398,7 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// -/// let attr = try!(fs::metadata("/some/file/path.txt")); +/// let attr = fs::metadata("/some/file/path.txt")?; /// // inspect attr ... /// # Ok(()) /// # } @@ -1184,6 +1415,7 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { /// This function currently corresponds to the `lstat` function on Unix /// and the `GetFileAttributesEx` function on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1200,7 +1432,7 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// -/// let attr = try!(fs::symlink_metadata("/some/file/path.txt")); +/// let attr = fs::symlink_metadata("/some/file/path.txt")?; /// // inspect attr ... /// # Ok(()) /// # } @@ -1226,6 +1458,7 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { /// on Windows, `from` can be anything, but `to` must *not* be a directory. /// /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1243,7 +1476,7 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::rename("a.txt", "b.txt")); // Rename a.txt to b.txt +/// fs::rename("a.txt", "b.txt")?; // Rename a.txt to b.txt /// # Ok(()) /// # } /// ``` @@ -1260,15 +1493,19 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> /// Note that if `from` and `to` both point to the same file, then the file /// will likely get truncated by this operation. /// -/// On success, the total number of bytes copied is returned. +/// On success, the total number of bytes copied is returned and it is equal to +/// the length of the `to` file as reported by `metadata`. /// /// # Platform-specific behavior /// /// This function currently corresponds to the `open` function in Unix /// with `O_RDONLY` for `from` and `O_WRONLY`, `O_CREAT`, and `O_TRUNC` for `to`. /// `O_CLOEXEC` is set for returned file descriptors. -/// On Windows, this function currently corresponds to `CopyFileEx`. +/// On Windows, this function currently corresponds to `CopyFileEx`. Alternate +/// NTFS streams are copied but only the size of the main stream is returned by +/// this function. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1287,7 +1524,7 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::copy("foo.txt", "bar.txt")); // Copy foo.txt to bar.txt +/// fs::copy("foo.txt", "bar.txt")?; // Copy foo.txt to bar.txt /// # Ok(()) } /// ``` #[stable(feature = "rust1", since = "1.0.0")] @@ -1305,6 +1542,7 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { /// This function currently corresponds to the `link` function on Unix /// and the `CreateHardLink` function on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1320,7 +1558,7 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::hard_link("a.txt", "b.txt")); // Hard link a.txt to b.txt +/// fs::hard_link("a.txt", "b.txt")?; // Hard link a.txt to b.txt /// # Ok(()) /// # } /// ``` @@ -1343,7 +1581,7 @@ pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<( /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::soft_link("a.txt", "b.txt")); +/// fs::soft_link("a.txt", "b.txt")?; /// # Ok(()) /// # } /// ``` @@ -1363,6 +1601,7 @@ pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<( /// and the `CreateFile` function with `FILE_FLAG_OPEN_REPARSE_POINT` and /// `FILE_FLAG_BACKUP_SEMANTICS` flags on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1379,7 +1618,7 @@ pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<( /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// let path = try!(fs::read_link("a.txt")); +/// let path = fs::read_link("a.txt")?; /// # Ok(()) /// # } /// ``` @@ -1396,6 +1635,7 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { /// This function currently corresponds to the `realpath` function on Unix /// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1412,7 +1652,7 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// let path = try!(fs::canonicalize("../a/../foo.txt")); +/// let path = fs::canonicalize("../a/../foo.txt")?; /// # Ok(()) /// # } /// ``` @@ -1428,6 +1668,7 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { /// This function currently corresponds to the `mkdir` function on Unix /// and the `CreateDirectory` function on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1444,7 +1685,7 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::create_dir("/some/dir")); +/// fs::create_dir("/some/dir")?; /// # Ok(()) /// # } /// ``` @@ -1461,6 +1702,7 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { /// This function currently corresponds to the `mkdir` function on Unix /// and the `CreateDirectory` function on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1473,13 +1715,19 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { /// error conditions for when a directory is being created (after it is /// determined to not exist) are outlined by `fs::create_dir`. /// +/// Notable exception is made for situations where any of the directories +/// specified in the `path` could not be created as it was being created concurrently. +/// Such cases are considered to be successful. That is, calling `create_dir_all` +/// concurrently from multiple threads or processes is guaranteed not to fail +/// due to a race condition with itself. +/// /// # Examples /// /// ``` /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::create_dir_all("/some/dir")); +/// fs::create_dir_all("/some/dir")?; /// # Ok(()) /// # } /// ``` @@ -1495,6 +1743,7 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// This function currently corresponds to the `rmdir` function on Unix /// and the `RemoveDirectory` function on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1511,7 +1760,7 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::remove_dir("/some/dir")); +/// fs::remove_dir("/some/dir")?; /// # Ok(()) /// # } /// ``` @@ -1532,6 +1781,7 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { /// and the `FindFirstFile`, `GetFileAttributesEx`, `DeleteFile`, and `RemoveDirectory` functions /// on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1544,7 +1794,7 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { /// use std::fs; /// /// # fn foo() -> std::io::Result<()> { -/// try!(fs::remove_dir_all("/some/dir")); +/// fs::remove_dir_all("/some/dir")?; /// # Ok(()) /// # } /// ``` @@ -1566,6 +1816,7 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// This function currently corresponds to the `opendir` function on Unix /// and the `FindFirstFile` function on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1587,11 +1838,11 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// // one possible implementation of walking a directory only visiting files /// fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> { /// if dir.is_dir() { -/// for entry in try!(fs::read_dir(dir)) { -/// let entry = try!(entry); +/// for entry in fs::read_dir(dir)? { +/// let entry = entry?; /// let path = entry.path(); /// if path.is_dir() { -/// try!(visit_dirs(&path, cb)); +/// visit_dirs(&path, cb)?; /// } else { /// cb(&entry); /// } @@ -1612,6 +1863,7 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { /// This function currently corresponds to the `chmod` function on Unix /// and the `SetFileAttributes` function on Windows. /// Note that, this [may change in the future][changes]. +/// /// [changes]: ../io/index.html#platform-specific-behavior /// /// # Errors @@ -1628,9 +1880,9 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { /// # fn foo() -> std::io::Result<()> { /// use std::fs; /// -/// let mut perms = try!(fs::metadata("foo.txt")).permissions(); +/// let mut perms = fs::metadata("foo.txt")?.permissions(); /// perms.set_readonly(true); -/// try!(fs::set_permissions("foo.txt", perms)); +/// fs::set_permissions("foo.txt", perms)?; /// # Ok(()) /// # } /// ``` @@ -1659,9 +1911,9 @@ impl DirBuilder { } } - /// Indicate that directories create should be created recursively, creating - /// all parent directories if they do not exist with the same security and - /// permissions settings. + /// Indicates that directories should be created recursively, creating all + /// parent directories. Parents that do not exist are created with the same + /// security and permissions settings. /// /// This option defaults to `false`. /// @@ -1682,6 +1934,9 @@ impl DirBuilder { /// Create the specified directory with the options configured in this /// builder. /// + /// It is considered an error if the directory already exists unless + /// recursive mode is enabled. + /// /// # Examples /// /// ```no_run @@ -1708,11 +1963,25 @@ impl DirBuilder { } fn create_dir_all(&self, path: &Path) -> io::Result<()> { - if path == Path::new("") || path.is_dir() { return Ok(()) } - if let Some(p) = path.parent() { - self.create_dir_all(p)? + if path == Path::new("") { + return Ok(()) + } + + match self.inner.mkdir(path) { + Ok(()) => return Ok(()), + Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} + Err(_) if path.is_dir() => return Ok(()), + Err(e) => return Err(e), + } + match path.parent() { + Some(p) => try!(self.create_dir_all(p)), + None => return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")), + } + match self.inner.mkdir(path) { + Ok(()) => Ok(()), + Err(_) if path.is_dir() => Ok(()), + Err(e) => Err(e), } - self.inner.mkdir(path) } } @@ -1722,7 +1991,7 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder { } } -#[cfg(all(test, not(target_os = "emscripten")))] +#[cfg(all(test, not(any(target_os = "cloudabi", target_os = "emscripten"))))] mod tests { use io::prelude::*; @@ -1732,6 +2001,7 @@ mod tests { use rand::{StdRng, Rng}; use str; use sys_common::io::test::{TempDir, tmpdir}; + use thread; #[cfg(windows)] use os::windows::fs::{symlink_dir, symlink_file}; @@ -1751,9 +2021,21 @@ mod tests { } ) } + #[cfg(windows)] macro_rules! error { ($e:expr, $s:expr) => ( match $e { Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), + Err(ref err) => assert!(err.raw_os_error() == Some($s), + format!("`{}` did not have a code of `{}`", err, $s)) + } + ) } + + #[cfg(unix)] + macro_rules! error { ($e:expr, $s:expr) => ( error_contains!($e, $s) ) } + + macro_rules! error_contains { ($e:expr, $s:expr) => ( + match $e { + Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s), Err(ref err) => assert!(err.to_string().contains($s), format!("`{}` did not contain `{}`", err, $s)) } @@ -1771,12 +2053,9 @@ mod tests { match symlink_file(r"nonexisting_target", link) { Ok(_) => true, - Err(ref err) => - if err.to_string().contains("A required privilege is not held by the client.") { - false - } else { - true - } + // ERROR_PRIVILEGE_NOT_HELD = 1314 + Err(ref err) if err.raw_os_error() == Some(1314) => false, + Err(_) => true, } } @@ -1807,12 +2086,10 @@ mod tests { let filename = &tmpdir.join("file_that_does_not_exist.txt"); let result = File::open(filename); - if cfg!(unix) { - error!(result, "No such file or directory"); - } - if cfg!(windows) { - error!(result, "The system cannot find the file specified"); - } + #[cfg(unix)] + error!(result, "No such file or directory"); + #[cfg(windows)] + error!(result, 2); // ERROR_FILE_NOT_FOUND } #[test] @@ -1822,12 +2099,10 @@ mod tests { let result = fs::remove_file(filename); - if cfg!(unix) { - error!(result, "No such file or directory"); - } - if cfg!(windows) { - error!(result, "The system cannot find the file specified"); - } + #[cfg(unix)] + error!(result, "No such file or directory"); + #[cfg(windows)] + error!(result, 2); // ERROR_FILE_NOT_FOUND } #[test] @@ -2010,6 +2285,27 @@ mod tests { } #[test] + #[cfg(unix)] + fn set_get_unix_permissions() { + use os::unix::fs::PermissionsExt; + + let tmpdir = tmpdir(); + let filename = &tmpdir.join("set_get_unix_permissions"); + check!(fs::create_dir(filename)); + let mask = 0o7777; + + check!(fs::set_permissions(filename, + fs::Permissions::from_mode(0))); + let metadata0 = check!(fs::metadata(filename)); + assert_eq!(mask & metadata0.permissions().mode(), 0); + + check!(fs::set_permissions(filename, + fs::Permissions::from_mode(0o1777))); + let metadata1 = check!(fs::metadata(filename)); + assert_eq!(mask & metadata1.permissions().mode(), 0o1777); + } + + #[test] #[cfg(windows)] fn file_test_io_seek_read_write() { use os::windows::fs::FileExt; @@ -2197,8 +2493,39 @@ mod tests { } #[test] + fn concurrent_recursive_mkdir() { + for _ in 0..100 { + let dir = tmpdir(); + let mut dir = dir.join("a"); + for _ in 0..40 { + dir = dir.join("a"); + } + let mut join = vec!(); + for _ in 0..8 { + let dir = dir.clone(); + join.push(thread::spawn(move || { + check!(fs::create_dir_all(&dir)); + })) + } + + // No `Display` on result of `join()` + join.drain(..).map(|join| join.join().unwrap()).count(); + } + } + + #[test] fn recursive_mkdir_slash() { - check!(fs::create_dir_all(&Path::new("/"))); + check!(fs::create_dir_all(Path::new("/"))); + } + + #[test] + fn recursive_mkdir_dot() { + check!(fs::create_dir_all(Path::new("."))); + } + + #[test] + fn recursive_mkdir_empty() { + check!(fs::create_dir_all(Path::new(""))); } #[test] @@ -2279,7 +2606,7 @@ mod tests { let tmpdir = tmpdir(); let unicode = tmpdir.path(); - let unicode = unicode.join(&format!("test-각丁ー再见")); + let unicode = unicode.join("test-각丁ー再见"); check!(fs::create_dir(&unicode)); assert!(unicode.exists()); assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); @@ -2386,7 +2713,7 @@ mod tests { fn copy_file_preserves_streams() { let tmp = tmpdir(); check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); - assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 6); + assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0); assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0); let mut v = Vec::new(); check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v)); @@ -2394,6 +2721,18 @@ mod tests { } #[test] + fn copy_file_returns_metadata_len() { + let tmp = tmpdir(); + let in_path = tmp.join("in.txt"); + let out_path = tmp.join("out.txt"); + check!(check!(File::create(&in_path)).write(b"lettuce")); + #[cfg(windows)] + check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot")); + let copied_len = check!(fs::copy(&in_path, &out_path)); + assert_eq!(check!(out_path.metadata()).len(), copied_len); + } + + #[test] fn symlinks_work() { let tmpdir = tmpdir(); if !got_symlink_permission(&tmpdir) { return }; @@ -2582,8 +2921,10 @@ mod tests { let mut a = OO::new(); a.append(true); let mut ra = OO::new(); ra.read(true).append(true); - let invalid_options = if cfg!(windows) { "The parameter is incorrect" } - else { "Invalid argument" }; + #[cfg(windows)] + let invalid_options = 87; // ERROR_INVALID_PARAMETER + #[cfg(unix)] + let invalid_options = "Invalid argument"; // Test various combinations of creation modes and access modes. // @@ -2702,6 +3043,27 @@ mod tests { } #[test] + fn write_then_read() { + let mut bytes = [0; 1024]; + StdRng::new().unwrap().fill_bytes(&mut bytes); + + let tmpdir = tmpdir(); + + check!(fs::write(&tmpdir.join("test"), &bytes[..])); + let v = check!(fs::read(&tmpdir.join("test"))); + assert!(v == &bytes[..]); + + check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF])); + error_contains!(fs::read_string(&tmpdir.join("not-utf8")), + "stream did not contain valid UTF-8"); + + let s = "𐁁𐀓𐀠𐀴𐀍"; + check!(fs::write(&tmpdir.join("utf8"), s.as_bytes())); + let string = check!(fs::read_string(&tmpdir.join("utf8"))); + assert_eq!(string, s); + } + + #[test] fn file_try_clone() { let tmpdir = tmpdir(); |