diff options
Diffstat (limited to 'vendor/tempfile/src')
-rw-r--r-- | vendor/tempfile/src/dir.rs | 31 | ||||
-rw-r--r-- | vendor/tempfile/src/file/imp/mod.rs | 4 | ||||
-rw-r--r-- | vendor/tempfile/src/file/imp/unix.rs | 41 | ||||
-rw-r--r-- | vendor/tempfile/src/file/mod.rs | 30 | ||||
-rw-r--r-- | vendor/tempfile/src/lib.rs | 41 | ||||
-rw-r--r-- | vendor/tempfile/src/spooled.rs | 42 | ||||
-rw-r--r-- | vendor/tempfile/src/util.rs | 17 |
7 files changed, 142 insertions, 64 deletions
diff --git a/vendor/tempfile/src/dir.rs b/vendor/tempfile/src/dir.rs index d6307627f..d5a944b6f 100644 --- a/vendor/tempfile/src/dir.rs +++ b/vendor/tempfile/src/dir.rs @@ -9,6 +9,7 @@ // except according to those terms. use remove_dir_all::remove_dir_all; +use std::mem; use std::path::{self, Path, PathBuf}; use std::{fmt, fs, io}; @@ -192,7 +193,7 @@ pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> { /// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html /// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html pub struct TempDir { - path: Option<PathBuf>, + path: Box<Path>, } impl TempDir { @@ -292,7 +293,7 @@ impl TempDir { /// # } /// ``` pub fn path(&self) -> &path::Path { - self.path.as_ref().unwrap() + self.path.as_ref() } /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located. @@ -322,8 +323,13 @@ impl TempDir { /// # Ok(()) /// # } /// ``` - pub fn into_path(mut self) -> PathBuf { - self.path.take().unwrap() + pub fn into_path(self) -> PathBuf { + // Prevent the Drop impl from being called. + let mut this = mem::ManuallyDrop::new(self); + + // replace this.path with an empty Box, since an empty Box does not + // allocate any heap memory. + mem::replace(&mut this.path, PathBuf::new().into_boxed_path()).into() } /// Closes and removes the temporary directory, returning a `Result`. @@ -369,8 +375,12 @@ impl TempDir { pub fn close(mut self) -> io::Result<()> { let result = remove_dir_all(self.path()).with_err_path(|| self.path()); - // Prevent the Drop impl from removing the dir a second time. - self.path = None; + // Set self.path to empty Box to release the memory, since an empty + // Box does not allocate any heap memory. + self.path = PathBuf::new().into_boxed_path(); + + // Prevent the Drop impl from being called. + mem::forget(self); result } @@ -392,15 +402,14 @@ impl fmt::Debug for TempDir { impl Drop for TempDir { fn drop(&mut self) { - // Path is `None` if `close()` or `into_path()` has been called. - if let Some(ref p) = self.path { - let _ = remove_dir_all(p); - } + let _ = remove_dir_all(self.path()); } } pub(crate) fn create(path: PathBuf) -> io::Result<TempDir> { fs::create_dir(&path) .with_err_path(|| &path) - .map(|_| TempDir { path: Some(path) }) + .map(|_| TempDir { + path: path.into_boxed_path(), + }) } diff --git a/vendor/tempfile/src/file/imp/mod.rs b/vendor/tempfile/src/file/imp/mod.rs index 31e872886..fbb2bbf63 100644 --- a/vendor/tempfile/src/file/imp/mod.rs +++ b/vendor/tempfile/src/file/imp/mod.rs @@ -1,5 +1,5 @@ -cfg_if! { - if #[cfg(any(unix, target_os = "redox"))] { +cfg_if::cfg_if! { + if #[cfg(any(unix, target_os = "redox", target_os = "wasi"))] { mod unix; pub use self::unix::*; } else if #[cfg(windows)] { diff --git a/vendor/tempfile/src/file/imp/unix.rs b/vendor/tempfile/src/file/imp/unix.rs index 35b1ddb1e..480743cf7 100644 --- a/vendor/tempfile/src/file/imp/unix.rs +++ b/vendor/tempfile/src/file/imp/unix.rs @@ -2,10 +2,18 @@ use std::env; use std::ffi::{CString, OsStr}; use std::fs::{self, File, OpenOptions}; use std::io; -use std::os::unix::ffi::OsStrExt; -use std::os::unix::fs::{MetadataExt, OpenOptionsExt}; -use std::path::Path; +cfg_if::cfg_if! { + if #[cfg(not(target_os = "wasi"))] { + use std::os::unix::ffi::OsStrExt; + use std::os::unix::fs::{MetadataExt, OpenOptionsExt}; + } else { + use std::os::wasi::ffi::OsStrExt; + #[cfg(feature = "nightly")] + use std::os::wasi::fs::MetadataExt; + } +} use crate::util; +use std::path::Path; #[cfg(not(target_os = "redox"))] use libc::{c_char, c_int, link, rename, unlink}; @@ -33,12 +41,14 @@ pub fn cstr(path: &Path) -> io::Result<CString> { } pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> { - open_options - .read(true) - .write(true) - .create_new(true) - .mode(0o600) - .open(path) + open_options.read(true).write(true).create_new(true); + + #[cfg(not(target_os = "wasi"))] + { + open_options.mode(0o600); + } + + open_options.open(path) } fn create_unlinked(path: &Path) -> io::Result<File> { @@ -60,11 +70,11 @@ fn create_unlinked(path: &Path) -> io::Result<File> { #[cfg(target_os = "linux")] pub fn create(dir: &Path) -> io::Result<File> { - use libc::{EISDIR, ENOENT, EOPNOTSUPP, O_EXCL, O_TMPFILE}; + use libc::{EISDIR, ENOENT, EOPNOTSUPP, O_TMPFILE}; OpenOptions::new() .read(true) .write(true) - .custom_flags(O_TMPFILE | O_EXCL) // do not mix with `create_new(true)` + .custom_flags(O_TMPFILE) // do not mix with `create_new(true)` .open(dir) .or_else(|e| { match e.raw_os_error() { @@ -90,6 +100,7 @@ fn create_unix(dir: &Path) -> io::Result<File> { ) } +#[cfg(any(not(target_os = "wasi"), feature = "nightly"))] pub fn reopen(file: &File, path: &Path) -> io::Result<File> { let new_file = OpenOptions::new().read(true).write(true).open(path)?; let old_meta = file.metadata()?; @@ -103,6 +114,14 @@ pub fn reopen(file: &File, path: &Path) -> io::Result<File> { Ok(new_file) } +#[cfg(all(target_os = "wasi", not(feature = "nightly")))] +pub fn reopen(_file: &File, _path: &Path) -> io::Result<File> { + return Err(io::Error::new( + io::ErrorKind::Other, + "this operation is supported on WASI only on nightly Rust (with `nightly` feature enabled)", + )); +} + #[cfg(not(target_os = "redox"))] pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> { unsafe { diff --git a/vendor/tempfile/src/file/mod.rs b/vendor/tempfile/src/file/mod.rs index 31fdd4bed..b859ced79 100644 --- a/vendor/tempfile/src/file/mod.rs +++ b/vendor/tempfile/src/file/mod.rs @@ -138,7 +138,7 @@ impl error::Error for PathPersistError { /// /// When dropped, the temporary file is deleted. pub struct TempPath { - path: PathBuf, + path: Box<Path>, } impl TempPath { @@ -176,8 +176,8 @@ impl TempPath { /// # } /// ``` pub fn close(mut self) -> io::Result<()> { - let result = fs::remove_file(&self.path).with_err_path(|| &self.path); - self.path = PathBuf::new(); + let result = fs::remove_file(&self.path).with_err_path(|| &*self.path); + self.path = PathBuf::new().into_boxed_path(); mem::forget(self); result } @@ -231,7 +231,7 @@ impl TempPath { // Don't drop `self`. We don't want to try deleting the old // temporary file path. (It'll fail, but the failure is never // seen.) - self.path = PathBuf::new(); + self.path = PathBuf::new().into_boxed_path(); mem::forget(self); Ok(()) } @@ -293,7 +293,7 @@ impl TempPath { // Don't drop `self`. We don't want to try deleting the old // temporary file path. (It'll fail, but the failure is never // seen.) - self.path = PathBuf::new(); + self.path = PathBuf::new().into_boxed_path(); mem::forget(self); Ok(()) } @@ -341,9 +341,9 @@ impl TempPath { // Don't drop `self`. We don't want to try deleting the old // temporary file path. (It'll fail, but the failure is never // seen.) - let path = mem::replace(&mut self.path, PathBuf::new()); + let path = mem::replace(&mut self.path, PathBuf::new().into_boxed_path()); mem::forget(self); - Ok(path) + Ok(path.into()) } Err(e) => Err(PathPersistError { error: e, @@ -351,6 +351,18 @@ impl TempPath { }), } } + + /// Create a new TempPath from an existing path. This can be done even if no + /// file exists at the given path. + /// + /// This is mostly useful for interacting with libraries and external + /// components that provide files to be consumed or expect a path with no + /// existing file to be given. + pub fn from_path(path: impl Into<PathBuf>) -> Self { + Self { + path: path.into().into_boxed_path(), + } + } } impl fmt::Debug for TempPath { @@ -953,7 +965,9 @@ pub(crate) fn create_named( imp::create_named(&path, open_options) .with_err_path(|| path.clone()) .map(|file| NamedTempFile { - path: TempPath { path }, + path: TempPath { + path: path.into_boxed_path(), + }, file, }) } diff --git a/vendor/tempfile/src/lib.rs b/vendor/tempfile/src/lib.rs index 51c2da09a..c38ca7b87 100644 --- a/vendor/tempfile/src/lib.rs +++ b/vendor/tempfile/src/lib.rs @@ -27,6 +27,42 @@ //! rely on file paths for _some_ operations. See the security documentation on //! the `NamedTempFile` type for more information. //! +//! ## Early drop pitfall +//! +//! Because `TempDir` and `NamedTempFile` rely on their destructors for cleanup, this can lead +//! to an unexpected early removal of the directory/file, usually when working with APIs which are +//! generic over `AsRef<Path>`. Consider the following example: +//! +//! ```no_run +//! # use tempfile::tempdir; +//! # use std::io; +//! # use std::process::Command; +//! # fn main() { +//! # if let Err(_) = run() { +//! # ::std::process::exit(1); +//! # } +//! # } +//! # fn run() -> Result<(), io::Error> { +//! // Create a directory inside of `std::env::temp_dir()`. +//! let temp_dir = tempdir()?; +//! +//! // Spawn the `touch` command inside the temporary directory and collect the exit status +//! // Note that `temp_dir` is **not** moved into `current_dir`, but passed as a reference +//! let exit_status = Command::new("touch").arg("tmp").current_dir(&temp_dir).status()?; +//! assert!(exit_status.success()); +//! +//! # Ok(()) +//! # } +//! ``` +//! +//! This works because a reference to `temp_dir` is passed to `current_dir`, resulting in the +//! destructor of `temp_dir` being run after the `Command` has finished execution. Moving the +//! `TempDir` into the `current_dir` call would result in the `TempDir` being converted into +//! an internal representation, with the original value being dropped and the directory thus +//! being deleted, before the command can be executed. +//! +//! The `touch` command would fail with an `No such file or directory` error. +//! //! ## Examples //! //! Create a temporary file and write some data into it: @@ -125,9 +161,10 @@ #![cfg_attr(test, deny(warnings))] #![deny(rust_2018_idioms)] #![allow(clippy::redundant_field_names)] +#![cfg_attr(feature = "nightly", feature(wasi_ext))] -#[macro_use] -extern crate cfg_if; +#[cfg(doctest)] +doc_comment::doctest!("../README.md"); const NUM_RETRIES: u32 = 1 << 31; const NUM_RAND_CHARS: usize = 6; diff --git a/vendor/tempfile/src/spooled.rs b/vendor/tempfile/src/spooled.rs index e6f750e58..ed6c16fb4 100644 --- a/vendor/tempfile/src/spooled.rs +++ b/vendor/tempfile/src/spooled.rs @@ -2,8 +2,9 @@ use crate::file::tempfile; use std::fs::File; use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; +/// A wrapper for the two states of a `SpooledTempFile`. #[derive(Debug)] -enum SpooledInner { +pub enum SpooledData { InMemory(Cursor<Vec<u8>>), OnDisk(File), } @@ -15,7 +16,7 @@ enum SpooledInner { #[derive(Debug)] pub struct SpooledTempFile { max_size: usize, - inner: SpooledInner, + inner: SpooledData, } /// Create a new spooled temporary file. @@ -66,15 +67,15 @@ impl SpooledTempFile { pub fn new(max_size: usize) -> SpooledTempFile { SpooledTempFile { max_size: max_size, - inner: SpooledInner::InMemory(Cursor::new(Vec::new())), + inner: SpooledData::InMemory(Cursor::new(Vec::new())), } } /// Returns true if the file has been rolled over to disk. pub fn is_rolled(&self) -> bool { match self.inner { - SpooledInner::InMemory(_) => false, - SpooledInner::OnDisk(_) => true, + SpooledData::InMemory(_) => false, + SpooledData::OnDisk(_) => true, } } @@ -83,11 +84,11 @@ impl SpooledTempFile { pub fn roll(&mut self) -> io::Result<()> { if !self.is_rolled() { let mut file = tempfile()?; - if let SpooledInner::InMemory(ref mut cursor) = self.inner { + if let SpooledData::InMemory(ref mut cursor) = self.inner { file.write_all(cursor.get_ref())?; file.seek(SeekFrom::Start(cursor.position()))?; } - self.inner = SpooledInner::OnDisk(file); + self.inner = SpooledData::OnDisk(file); } Ok(()) } @@ -97,20 +98,25 @@ impl SpooledTempFile { self.roll()?; // does nothing if already rolled over } match self.inner { - SpooledInner::InMemory(ref mut cursor) => { + SpooledData::InMemory(ref mut cursor) => { cursor.get_mut().resize(size as usize, 0); Ok(()) } - SpooledInner::OnDisk(ref mut file) => file.set_len(size), + SpooledData::OnDisk(ref mut file) => file.set_len(size), } } + + /// Consumes and returns the inner `SpooledData` type. + pub fn into_inner(self) -> SpooledData { + self.inner + } } impl Read for SpooledTempFile { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { match self.inner { - SpooledInner::InMemory(ref mut cursor) => cursor.read(buf), - SpooledInner::OnDisk(ref mut file) => file.read(buf), + SpooledData::InMemory(ref mut cursor) => cursor.read(buf), + SpooledData::OnDisk(ref mut file) => file.read(buf), } } } @@ -119,7 +125,7 @@ impl Write for SpooledTempFile { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { // roll over to file if necessary let mut rolling = false; - if let SpooledInner::InMemory(ref mut cursor) = self.inner { + if let SpooledData::InMemory(ref mut cursor) = self.inner { rolling = cursor.position() as usize + buf.len() > self.max_size; } if rolling { @@ -128,16 +134,16 @@ impl Write for SpooledTempFile { // write the bytes match self.inner { - SpooledInner::InMemory(ref mut cursor) => cursor.write(buf), - SpooledInner::OnDisk(ref mut file) => file.write(buf), + SpooledData::InMemory(ref mut cursor) => cursor.write(buf), + SpooledData::OnDisk(ref mut file) => file.write(buf), } } #[inline] fn flush(&mut self) -> io::Result<()> { match self.inner { - SpooledInner::InMemory(ref mut cursor) => cursor.flush(), - SpooledInner::OnDisk(ref mut file) => file.flush(), + SpooledData::InMemory(ref mut cursor) => cursor.flush(), + SpooledData::OnDisk(ref mut file) => file.flush(), } } } @@ -145,8 +151,8 @@ impl Write for SpooledTempFile { impl Seek for SpooledTempFile { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { match self.inner { - SpooledInner::InMemory(ref mut cursor) => cursor.seek(pos), - SpooledInner::OnDisk(ref mut file) => file.seek(pos), + SpooledData::InMemory(ref mut cursor) => cursor.seek(pos), + SpooledData::OnDisk(ref mut file) => file.seek(pos), } } } diff --git a/vendor/tempfile/src/util.rs b/vendor/tempfile/src/util.rs index aa76bb256..8c91b9c69 100644 --- a/vendor/tempfile/src/util.rs +++ b/vendor/tempfile/src/util.rs @@ -1,23 +1,16 @@ -use rand::distributions::Alphanumeric; -use rand::{self, Rng}; +use fastrand; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; -use std::{io, str}; +use std::{io, iter::repeat_with}; use crate::error::IoResultExt; fn tmpname(prefix: &OsStr, suffix: &OsStr, rand_len: usize) -> OsString { let mut buf = OsString::with_capacity(prefix.len() + suffix.len() + rand_len); buf.push(prefix); - - // Push each character in one-by-one. Unfortunately, this is the only - // safe(ish) simple way to do this without allocating a temporary - // String/Vec. - unsafe { - rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(rand_len) - .for_each(|b| buf.push(str::from_utf8_unchecked(&[b as u8]))) + let mut char_buf = [0u8; 4]; + for c in repeat_with(fastrand::alphanumeric).take(rand_len) { + buf.push(c.encode_utf8(&mut char_buf)); } buf.push(suffix); buf |