diff options
Diffstat (limited to 'vendor/tempfile')
-rw-r--r-- | vendor/tempfile/.cargo-checksum.json | 2 | ||||
-rw-r--r-- | vendor/tempfile/Cargo.toml | 40 | ||||
-rw-r--r-- | vendor/tempfile/NEWS | 46 | ||||
-rw-r--r-- | vendor/tempfile/README.md | 3 | ||||
-rw-r--r-- | vendor/tempfile/src/dir.rs | 14 | ||||
-rw-r--r-- | vendor/tempfile/src/file/imp/other.rs | 4 | ||||
-rw-r--r-- | vendor/tempfile/src/file/imp/unix.rs | 85 | ||||
-rw-r--r-- | vendor/tempfile/src/file/imp/windows.rs | 13 | ||||
-rw-r--r-- | vendor/tempfile/src/file/mod.rs | 151 | ||||
-rw-r--r-- | vendor/tempfile/src/lib.rs | 176 | ||||
-rw-r--r-- | vendor/tempfile/src/spooled.rs | 5 | ||||
-rw-r--r-- | vendor/tempfile/src/util.rs | 10 | ||||
-rw-r--r-- | vendor/tempfile/tests/namedtempfile.rs | 144 | ||||
-rw-r--r-- | vendor/tempfile/tests/tempdir.rs | 8 | ||||
-rw-r--r-- | vendor/tempfile/tests/tempfile.rs | 7 |
15 files changed, 547 insertions, 161 deletions
diff --git a/vendor/tempfile/.cargo-checksum.json b/vendor/tempfile/.cargo-checksum.json index 26f8560bc..6633ccc75 100644 --- a/vendor/tempfile/.cargo-checksum.json +++ b/vendor/tempfile/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"685243e302f6e014de9c8e9b95596e5f63c7bf7fde42e8e66a41a6bc7fd5e803","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"8b427f5bc501764575e52ba4f9d95673cf8f6d80a86d0d06599852e1a9a20a36","NEWS":"4255c86ac140a4d08423cd05cbd0aa42ff796bb4b38579dd19cde289ee3baecd","README.md":"db6717cbd0b3cbbce5f3cdb8a80d8f2d90b1be251b4c1c647557ae0f78ec9748","src/dir.rs":"4499ff439b740f8d2f01458664e2bf72bbfdd1206226780c6a91fb309ef15707","src/error.rs":"cc7d8eace0fff11cb342158d2885d5637bfb14b24ef30755e808554772039c5f","src/file/imp/mod.rs":"f6da9fcd93f11889670a251fdd8231b5f4614e5a971b7b183f52b44af68568d5","src/file/imp/other.rs":"99c8f9f3251199fc31e7b88810134712e5725fb6fa14648696ed5cbea980fc5b","src/file/imp/unix.rs":"cf8eeceecfddc37c9eaf95a1ebe088314dc468f07fe357961d80817eef619ca4","src/file/imp/windows.rs":"03d81d71c404f0d448e1162825d6fbd57a78b4af8d4dc5287ec2e7c5a873d7cc","src/file/mod.rs":"bda4ee3998106089a4c0ccbc8e46dc22b7d3aec427487fd4e414fb132b378736","src/lib.rs":"e2b0df7e17cc6680a5bb0829d0433f069c6bf9eede2007d21e3b01a595df41a8","src/spooled.rs":"51fa1d7639027234e257d343a5d3c95f2e47899ba6a24f0abec8d4d729eba6d6","src/util.rs":"2bd80ee69009e7e36b596d0105bb00184cff04e899e9fcce2e4cc21f23dda073","tests/namedtempfile.rs":"0031cb33ae6faf45be103869b4d98af63bef4040dc489b323212eb7a7ef72a9a","tests/spooled.rs":"29e797d486d867cb6ac46d4cf126eb5868a069a4070c3f50ffa02fbb0b887934","tests/tempdir.rs":"771d555d4eaa410207d212eb3744e016e0b5a22f1f1b7199636a4fac5daaf952","tests/tempfile.rs":"92078a1e20a39af77c1daa9a422345d20c41584dd2010b4829911c8741d1c628"},"package":"5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"}
\ No newline at end of file +{"files":{"Cargo.toml":"752e39e58b556c9d16d42fdfc7f772bbe853882bf4481cfe18922599c4bfbc04","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"8b427f5bc501764575e52ba4f9d95673cf8f6d80a86d0d06599852e1a9a20a36","NEWS":"5508856b86b8e1bf4f84c8df7fe3374968bed66f8585b8108d081cc67d425108","README.md":"5fb03bad7838354c686bab80e30323385f07efeb2aa1c5c35bdff7ec203dc5fa","src/dir.rs":"7efb94008d9d14dd3c19c105f2a0dd0695e102a1057ad91d80eed2bcb97e5bd0","src/error.rs":"cc7d8eace0fff11cb342158d2885d5637bfb14b24ef30755e808554772039c5f","src/file/imp/mod.rs":"f6da9fcd93f11889670a251fdd8231b5f4614e5a971b7b183f52b44af68568d5","src/file/imp/other.rs":"501cd1b444a5821127ea831fc8018706148f2d9f47c478f502b069963a42a2c7","src/file/imp/unix.rs":"c27c8c4e8b00e8a0e06c7243f072265eba8bc8e519e7b28ddf64d02cb4d99d01","src/file/imp/windows.rs":"3c064beb4b70677929bfe6372dba113756fbd8ea27aa6a36e86090d7b860f566","src/file/mod.rs":"10c141b5467b36f913f4946716d1d361d5a29bd7bf97ba8b6bb020d748ea0821","src/lib.rs":"6303e7470c680ad785f32eb717de2e512b88c2c5da0e1684e3704471fabd7398","src/spooled.rs":"05eef6a7aa9441ab834e602c0dd3df2222dcd4bcca91c5dfbc88591fb61a391f","src/util.rs":"6761c241cc6f4b99a85e45a827acd26b4d3bdf1d4efcb43a277f788b262ce0dc","tests/namedtempfile.rs":"a37c82908ed5df89cd2800c55abadf631d04caa55f09c966c8d1fd7a559a0ba6","tests/spooled.rs":"29e797d486d867cb6ac46d4cf126eb5868a069a4070c3f50ffa02fbb0b887934","tests/tempdir.rs":"abf08594f9d9ddc6c417de413bf63f9026150378af319d857e5ac8578f3fb665","tests/tempfile.rs":"9a2f8142151a6aa2fd047aa3749f9982ece4b080a3ace0d3c58d6bdb3f883c81"},"package":"b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"}
\ No newline at end of file diff --git a/vendor/tempfile/Cargo.toml b/vendor/tempfile/Cargo.toml index 253f8667b..85006d831 100644 --- a/vendor/tempfile/Cargo.toml +++ b/vendor/tempfile/Cargo.toml @@ -11,33 +11,49 @@ [package] edition = "2018" +rust-version = "1.48" name = "tempfile" -version = "3.3.0" -authors = ["Steven Allen <steven@stebalien.com>", "The Rust Project Developers", "Ashley Mannix <ashleymannix@live.com.au>", "Jason White <jasonaw0@gmail.com>"] -exclude = ["/.travis.yml", "/appveyor.yml"] +version = "3.5.0" +authors = [ + "Steven Allen <steven@stebalien.com>", + "The Rust Project Developers", + "Ashley Mannix <ashleymannix@live.com.au>", + "Jason White <me@jasonwhite.io>", +] description = "A library for managing temporary files and directories." -homepage = "http://stebalien.com/projects/tempfile-rs" +homepage = "https://stebalien.com/projects/tempfile-rs/" documentation = "https://docs.rs/tempfile" -keywords = ["tempfile", "tmpfile", "filesystem"] +readme = "README.md" +keywords = [ + "tempfile", + "tmpfile", + "filesystem", +] license = "MIT OR Apache-2.0" repository = "https://github.com/Stebalien/tempfile" + [dependencies.cfg-if] version = "1" [dependencies.fastrand] version = "1.6.0" -[dependencies.remove_dir_all] -version = "0.5" [dev-dependencies.doc-comment] version = "0.3" [features] nightly = [] -[target."cfg(any(unix, target_os = \"wasi\"))".dependencies.libc] -version = "0.2.27" + +[target."cfg(any(unix, target_os = \"wasi\"))".dependencies.rustix] +version = "0.37.1" +features = ["fs"] + [target."cfg(target_os = \"redox\")".dependencies.redox_syscall] -version = "0.2.9" -[target."cfg(windows)".dependencies.winapi] version = "0.3" -features = ["fileapi", "handleapi", "winbase"] + +[target."cfg(windows)".dependencies.windows-sys] +version = "0.45" +features = [ + "Win32_Storage_FileSystem", + "Win32_Foundation", +] diff --git a/vendor/tempfile/NEWS b/vendor/tempfile/NEWS index c28442426..14f8a9e4e 100644 --- a/vendor/tempfile/NEWS +++ b/vendor/tempfile/NEWS @@ -1,3 +1,47 @@ +3.5.0 +===== + +- Update rustix from 0.36 to 0.37.1. This makes wasi work on rust stable +- Update `windows-sys`, `redox_syscall` +- BREAKING: Remove the implementation of `Write for &NamedTempFile<F> where &F: Write`. Unfortunately, this can cause compile issues in unrelated code (https://github.com/Stebalien/tempfile/issues/224). + +3.4.0 +===== + +SECURITY: Prior `tempfile` releases depended on `remove_dir_all` version 0.5.0 which was vulnerable to a [TOCTOU race](https://github.com/XAMPPRocky/remove_dir_all/security/advisories/GHSA-mc8h-8q98-g5hr). This same race is present in rust versions prior to 1.58.1. + +Features: + +* Generalized temporary files: `NamedTempFile` can now abstract over different kinds of files (e.g., + unix domain sockets, pipes, etc.): + * Add `Builder::make` and `Builder::make_in` for generalized temp file + creation. + * Add `NamedTempFile::from_parts` to complement `NamedTempFile::into_parts`. + * Add generic parameter to `NamedTempFile` to support wrapping non-File types. + +Bug Fixes/Improvements: + +* Don't try to create a temporary file multiple times if the file path has been fully specified by + the user (no random characters). +* `NamedTempFile::persist_noclobber` is now always atomic on linux when `renameat_with` is + supported. Previously, it would first link the new path, then unlink the previous path. +* Fix compiler warnings on windows. + +Trivia: + +* Switch from `libc` to `rustix` on wasi/unix. This now makes direct syscalls instead of calling + through libc. +* Remove `remove_dir_all` dependency. The rust standard library has optimized their internal version + significantly. + * Switch to official windows-sys windows bindings. + +Breaking: + + * The minimum rust version is now `1.48.0`. + * Mark most functions as `must_use`. + * Uses direct syscalls on linux by default, instead of libc. + * The new type parameter in `NamedTempFile` may lead to type inference issues in some cases. + 3.3.0 ===== @@ -198,7 +242,7 @@ Add LFS Support. 2.0.0 ===== -This release replaces `TempFile` with a `tempfile()` function that returnes +This release replaces `TempFile` with a `tempfile()` function that returns `std::fs::File` objects. These are significantly more useful because most rust libraries expect normal `File` objects. diff --git a/vendor/tempfile/README.md b/vendor/tempfile/README.md index 1dba3a01d..bf207f8a4 100644 --- a/vendor/tempfile/README.md +++ b/vendor/tempfile/README.md @@ -14,9 +14,10 @@ patterns and surprisingly difficult to implement securely). Usage ----- -Minimum required Rust version: 1.40.0 +Minimum required Rust version: 1.48.0 Add this to your `Cargo.toml`: + ```toml [dependencies] tempfile = "3" diff --git a/vendor/tempfile/src/dir.rs b/vendor/tempfile/src/dir.rs index d5a944b6f..917e47ec2 100644 --- a/vendor/tempfile/src/dir.rs +++ b/vendor/tempfile/src/dir.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use remove_dir_all::remove_dir_all; +use std::fs::remove_dir_all; use std::mem; use std::path::{self, Path, PathBuf}; use std::{fmt, fs, io}; @@ -65,9 +65,9 @@ pub fn tempdir() -> io::Result<TempDir> { TempDir::new() } -/// Create a new temporary directory. +/// Create a new temporary directory in a specific directory. /// -/// The `tempdir` function creates a directory in the file system +/// The `tempdir_in` function creates a directory in the specified directory /// and returns a [`TempDir`]. /// The directory will be automatically deleted when the `TempDir`s /// destructor is run. @@ -83,7 +83,7 @@ pub fn tempdir() -> io::Result<TempDir> { /// # Examples /// /// ``` -/// use tempfile::tempdir; +/// use tempfile::tempdir_in; /// use std::fs::File; /// use std::io::{self, Write}; /// @@ -93,8 +93,8 @@ pub fn tempdir() -> io::Result<TempDir> { /// # } /// # } /// # fn run() -> Result<(), io::Error> { -/// // Create a directory inside of `std::env::temp_dir()`, -/// let dir = tempdir()?; +/// // Create a directory inside of the current directory. +/// let dir = tempdir_in(".")?; /// /// let file_path = dir.path().join("my-temporary-note.txt"); /// let mut file = File::create(file_path)?; @@ -292,6 +292,7 @@ impl TempDir { /// # Ok(()) /// # } /// ``` + #[must_use] pub fn path(&self) -> &path::Path { self.path.as_ref() } @@ -323,6 +324,7 @@ impl TempDir { /// # Ok(()) /// # } /// ``` + #[must_use] pub fn into_path(self) -> PathBuf { // Prevent the Drop impl from being called. let mut this = mem::ManuallyDrop::new(self); diff --git a/vendor/tempfile/src/file/imp/other.rs b/vendor/tempfile/src/file/imp/other.rs index d8a55a745..8721d2da6 100644 --- a/vendor/tempfile/src/file/imp/other.rs +++ b/vendor/tempfile/src/file/imp/other.rs @@ -9,7 +9,7 @@ fn not_supported<T>() -> io::Result<T> { )) } -pub fn create_named(_path: &Path, open_options: &mut OpenOptions) -> io::Result<File> { +pub fn create_named(_path: &Path, _open_options: &mut OpenOptions) -> io::Result<File> { not_supported() } @@ -25,6 +25,6 @@ pub fn persist(_old_path: &Path, _new_path: &Path, _overwrite: bool) -> io::Resu not_supported() } -pub fn keep(path: &Path) -> io::Result<()> { +pub fn keep(_path: &Path) -> io::Result<()> { not_supported() } diff --git a/vendor/tempfile/src/file/imp/unix.rs b/vendor/tempfile/src/file/imp/unix.rs index 480743cf7..c305ea95e 100644 --- a/vendor/tempfile/src/file/imp/unix.rs +++ b/vendor/tempfile/src/file/imp/unix.rs @@ -1,13 +1,11 @@ use std::env; -use std::ffi::{CString, OsStr}; +use std::ffi::OsStr; use std::fs::{self, File, OpenOptions}; use std::io; 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; } @@ -16,29 +14,7 @@ use crate::util; use std::path::Path; #[cfg(not(target_os = "redox"))] -use libc::{c_char, c_int, link, rename, unlink}; - -#[cfg(not(target_os = "redox"))] -#[inline(always)] -pub fn cvt_err(result: c_int) -> io::Result<c_int> { - if result == -1 { - Err(io::Error::last_os_error()) - } else { - Ok(result) - } -} - -#[cfg(target_os = "redox")] -#[inline(always)] -pub fn cvt_err(result: Result<usize, syscall::Error>) -> io::Result<usize> { - result.map_err(|err| io::Error::from_raw_os_error(err.errno)) -} - -// Stolen from std. -pub fn cstr(path: &Path) -> io::Result<CString> { - CString::new(path.as_os_str().as_bytes()) - .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contained a null")) -} +use rustix::fs::{cwd, linkat, renameat, unlinkat, AtFlags}; pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> { open_options.read(true).write(true).create_new(true); @@ -70,16 +46,18 @@ 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_TMPFILE}; + use rustix::{fs::OFlags, io::Errno}; OpenOptions::new() .read(true) .write(true) - .custom_flags(O_TMPFILE) // do not mix with `create_new(true)` + .custom_flags(OFlags::TMPFILE.bits() as i32) // do not mix with `create_new(true)` .open(dir) .or_else(|e| { - match e.raw_os_error() { + match Errno::from_io_error(&e) { // These are the three "not supported" error codes for O_TMPFILE. - Some(EOPNOTSUPP) | Some(EISDIR) | Some(ENOENT) => create_unix(dir), + Some(Errno::OPNOTSUPP) | Some(Errno::ISDIR) | Some(Errno::NOENT) => { + create_unix(dir) + } _ => Err(e), } }) @@ -124,29 +102,40 @@ pub fn reopen(_file: &File, _path: &Path) -> io::Result<File> { #[cfg(not(target_os = "redox"))] pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> { - unsafe { - let old_path = cstr(old_path)?; - let new_path = cstr(new_path)?; - if overwrite { - cvt_err(rename( - old_path.as_ptr() as *const c_char, - new_path.as_ptr() as *const c_char, - ))?; - } else { - cvt_err(link( - old_path.as_ptr() as *const c_char, - new_path.as_ptr() as *const c_char, - ))?; - // Ignore unlink errors. Can we do better? - // On recent linux, we can use renameat2 to do this atomically. - let _ = unlink(old_path.as_ptr() as *const c_char); + if overwrite { + renameat(cwd(), old_path, cwd(), new_path)?; + } else { + // On Linux, use `renameat_with` to avoid overwriting an existing name, + // if the kernel and the filesystem support it. + #[cfg(any(target_os = "android", target_os = "linux"))] + { + use rustix::fs::{renameat_with, RenameFlags}; + use rustix::io::Errno; + use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; + + static NOSYS: AtomicBool = AtomicBool::new(false); + if !NOSYS.load(Relaxed) { + match renameat_with(cwd(), old_path, cwd(), new_path, RenameFlags::NOREPLACE) { + Ok(()) => return Ok(()), + Err(Errno::NOSYS) => NOSYS.store(true, Relaxed), + Err(Errno::INVAL) => {} + Err(e) => return Err(e.into()), + } + } } - Ok(()) + + // Otherwise use `linkat` to create the new filesystem name, which + // will fail if the name already exists, and then `unlinkat` to remove + // the old name. + linkat(cwd(), old_path, cwd(), new_path, AtFlags::empty())?; + // Ignore unlink errors. Can we do better? + let _ = unlinkat(cwd(), old_path, AtFlags::empty()); } + Ok(()) } #[cfg(target_os = "redox")] -pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> { +pub fn persist(_old_path: &Path, _new_path: &Path, _overwrite: bool) -> io::Result<()> { // XXX implement when possible Err(io::Error::from_raw_os_error(syscall::ENOSYS)) } diff --git a/vendor/tempfile/src/file/imp/windows.rs b/vendor/tempfile/src/file/imp/windows.rs index 71b474880..cb2673b5a 100644 --- a/vendor/tempfile/src/file/imp/windows.rs +++ b/vendor/tempfile/src/file/imp/windows.rs @@ -6,13 +6,12 @@ use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle}; use std::path::Path; use std::{io, iter}; -use winapi::um::fileapi::SetFileAttributesW; -use winapi::um::handleapi::INVALID_HANDLE_VALUE; -use winapi::um::winbase::{MoveFileExW, ReOpenFile}; -use winapi::um::winbase::{FILE_FLAG_DELETE_ON_CLOSE, MOVEFILE_REPLACE_EXISTING}; -use winapi::um::winnt::{FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY}; -use winapi::um::winnt::{FILE_GENERIC_READ, FILE_GENERIC_WRITE, HANDLE}; -use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE}; +use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE}; +use windows_sys::Win32::Storage::FileSystem::{ + MoveFileExW, ReOpenFile, SetFileAttributesW, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY, + FILE_FLAG_DELETE_ON_CLOSE, FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_SHARE_DELETE, + FILE_SHARE_READ, FILE_SHARE_WRITE, MOVEFILE_REPLACE_EXISTING, +}; use crate::util; diff --git a/vendor/tempfile/src/file/mod.rs b/vendor/tempfile/src/file/mod.rs index b859ced79..023acd26a 100644 --- a/vendor/tempfile/src/file/mod.rs +++ b/vendor/tempfile/src/file/mod.rs @@ -52,7 +52,7 @@ mod imp; /// /// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html pub fn tempfile() -> io::Result<File> { - tempfile_in(&env::temp_dir()) + tempfile_in(env::temp_dir()) } /// Create a new temporary file in the specified directory. @@ -467,29 +467,31 @@ impl AsRef<OsStr> for TempPath { /// # Resource Leaking /// /// If the program exits before the `NamedTempFile` destructor is -/// run, such as via [`std::process::exit()`], by segfaulting, or by -/// receiving a signal like `SIGINT`, then the temporary file -/// will not be deleted. +/// run, the temporary file will not be deleted. This can happen +/// if the process exits using [`std::process::exit()`], a segfault occurs, +/// receiving an interrupt signal like `SIGINT` that is not handled, or by using +/// a statically declared `NamedTempFile` instance (like with [`lazy_static`]). /// -/// Use the [`tempfile()`] function unless you absolutely need a named file. +/// Use the [`tempfile()`] function unless you need a named file path. /// /// [`tempfile()`]: fn.tempfile.html /// [`NamedTempFile::new()`]: #method.new /// [`NamedTempFile::new_in()`]: #method.new_in /// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html /// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html -pub struct NamedTempFile { +/// [`lazy_static`]: https://github.com/rust-lang-nursery/lazy-static.rs/issues/62 +pub struct NamedTempFile<F = File> { path: TempPath, - file: File, + file: F, } -impl fmt::Debug for NamedTempFile { +impl<F> fmt::Debug for NamedTempFile<F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NamedTempFile({:?})", self.path) } } -impl AsRef<Path> for NamedTempFile { +impl<F> AsRef<Path> for NamedTempFile<F> { #[inline] fn as_ref(&self) -> &Path { self.path() @@ -497,41 +499,46 @@ impl AsRef<Path> for NamedTempFile { } /// Error returned when persisting a temporary file fails. -#[derive(Debug)] -pub struct PersistError { +pub struct PersistError<F = File> { /// The underlying IO error. pub error: io::Error, /// The temporary file that couldn't be persisted. - pub file: NamedTempFile, + pub file: NamedTempFile<F>, +} + +impl<F> fmt::Debug for PersistError<F> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PersistError({:?})", self.error) + } } -impl From<PersistError> for io::Error { +impl<F> From<PersistError<F>> for io::Error { #[inline] - fn from(error: PersistError) -> io::Error { + fn from(error: PersistError<F>) -> io::Error { error.error } } -impl From<PersistError> for NamedTempFile { +impl<F> From<PersistError<F>> for NamedTempFile<F> { #[inline] - fn from(error: PersistError) -> NamedTempFile { + fn from(error: PersistError<F>) -> NamedTempFile<F> { error.file } } -impl fmt::Display for PersistError { +impl<F> fmt::Display for PersistError<F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "failed to persist temporary file: {}", self.error) } } -impl error::Error for PersistError { +impl<F> error::Error for PersistError<F> { fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(&self.error) } } -impl NamedTempFile { +impl NamedTempFile<File> { /// Create a new named temporary file. /// /// See [`Builder`] for more configuration. @@ -601,7 +608,9 @@ impl NamedTempFile { pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<NamedTempFile> { Builder::new().tempfile_in(dir) } +} +impl<F> NamedTempFile<F> { /// Get the temporary file's path. /// /// # Security @@ -711,7 +720,7 @@ impl NamedTempFile { /// ``` /// /// [`PersistError`]: struct.PersistError.html - pub fn persist<P: AsRef<Path>>(self, new_path: P) -> Result<File, PersistError> { + pub fn persist<P: AsRef<Path>>(self, new_path: P) -> Result<F, PersistError<F>> { let NamedTempFile { path, file } = self; match path.persist(new_path) { Ok(_) => Ok(file), @@ -764,7 +773,7 @@ impl NamedTempFile { /// # Ok(()) /// # } /// ``` - pub fn persist_noclobber<P: AsRef<Path>>(self, new_path: P) -> Result<File, PersistError> { + pub fn persist_noclobber<P: AsRef<Path>>(self, new_path: P) -> Result<F, PersistError<F>> { let NamedTempFile { path, file } = self; match path.persist_noclobber(new_path) { Ok(_) => Ok(file), @@ -808,7 +817,7 @@ impl NamedTempFile { /// ``` /// /// [`PathPersistError`]: struct.PathPersistError.html - pub fn keep(self) -> Result<(File, PathBuf), PersistError> { + pub fn keep(self) -> Result<(F, PathBuf), PersistError<F>> { let (file, path) = (self.file, self.path); match path.keep() { Ok(path) => Ok((file, path)), @@ -819,6 +828,49 @@ impl NamedTempFile { } } + /// Get a reference to the underlying file. + pub fn as_file(&self) -> &F { + &self.file + } + + /// Get a mutable reference to the underlying file. + pub fn as_file_mut(&mut self) -> &mut F { + &mut self.file + } + + /// Convert the temporary file into a `std::fs::File`. + /// + /// The inner file will be deleted. + pub fn into_file(self) -> F { + self.file + } + + /// Closes the file, leaving only the temporary file path. + /// + /// This is useful when another process must be able to open the temporary + /// file. + pub fn into_temp_path(self) -> TempPath { + self.path + } + + /// Converts the named temporary file into its constituent parts. + /// + /// Note: When the path is dropped, the file is deleted but the file handle + /// is still usable. + pub fn into_parts(self) -> (F, TempPath) { + (self.file, self.path) + } + + /// Creates a `NamedTempFile` from its constituent parts. + /// + /// This can be used with [`NamedTempFile::into_parts`] to reconstruct the + /// `NamedTempFile`. + pub fn from_parts(file: F, path: TempPath) -> Self { + Self { file, path } + } +} + +impl NamedTempFile<File> { /// Securely reopen the temporary file. /// /// This function is useful when you need multiple independent handles to @@ -858,54 +910,21 @@ impl NamedTempFile { imp::reopen(self.as_file(), NamedTempFile::path(self)) .with_err_path(|| NamedTempFile::path(self)) } - - /// Get a reference to the underlying file. - pub fn as_file(&self) -> &File { - &self.file - } - - /// Get a mutable reference to the underlying file. - pub fn as_file_mut(&mut self) -> &mut File { - &mut self.file - } - - /// Convert the temporary file into a `std::fs::File`. - /// - /// The inner file will be deleted. - pub fn into_file(self) -> File { - self.file - } - - /// Closes the file, leaving only the temporary file path. - /// - /// This is useful when another process must be able to open the temporary - /// file. - pub fn into_temp_path(self) -> TempPath { - self.path - } - - /// Converts the named temporary file into its constituent parts. - /// - /// Note: When the path is dropped, the file is deleted but the file handle - /// is still usable. - pub fn into_parts(self) -> (File, TempPath) { - (self.file, self.path) - } } -impl Read for NamedTempFile { +impl<F: Read> Read for NamedTempFile<F> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.as_file_mut().read(buf).with_err_path(|| self.path()) } } -impl<'a> Read for &'a NamedTempFile { +impl Read for &NamedTempFile<File> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.as_file().read(buf).with_err_path(|| self.path()) } } -impl Write for NamedTempFile { +impl<F: Write> Write for NamedTempFile<F> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.as_file_mut().write(buf).with_err_path(|| self.path()) } @@ -915,7 +934,7 @@ impl Write for NamedTempFile { } } -impl<'a> Write for &'a NamedTempFile { +impl Write for &NamedTempFile<File> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.as_file().write(buf).with_err_path(|| self.path()) } @@ -925,20 +944,23 @@ impl<'a> Write for &'a NamedTempFile { } } -impl Seek for NamedTempFile { +impl<F: Seek> Seek for NamedTempFile<F> { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { self.as_file_mut().seek(pos).with_err_path(|| self.path()) } } -impl<'a> Seek for &'a NamedTempFile { +impl Seek for &NamedTempFile<File> { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { self.as_file().seek(pos).with_err_path(|| self.path()) } } #[cfg(unix)] -impl std::os::unix::io::AsRawFd for NamedTempFile { +impl<F> std::os::unix::io::AsRawFd for NamedTempFile<F> +where + F: std::os::unix::io::AsRawFd, +{ #[inline] fn as_raw_fd(&self) -> std::os::unix::io::RawFd { self.as_file().as_raw_fd() @@ -946,7 +968,10 @@ impl std::os::unix::io::AsRawFd for NamedTempFile { } #[cfg(windows)] -impl std::os::windows::io::AsRawHandle for NamedTempFile { +impl<F> std::os::windows::io::AsRawHandle for NamedTempFile<F> +where + F: std::os::windows::io::AsRawHandle, +{ #[inline] fn as_raw_handle(&self) -> std::os::windows::io::RawHandle { self.as_file().as_raw_handle() diff --git a/vendor/tempfile/src/lib.rs b/vendor/tempfile/src/lib.rs index c38ca7b87..4b6371d49 100644 --- a/vendor/tempfile/src/lib.rs +++ b/vendor/tempfile/src/lib.rs @@ -14,9 +14,12 @@ //! //! ## Resource Leaking //! -//! `tempfile` will (almost) never fail to cleanup temporary resources, but `TempDir` and `NamedTempFile` will if -//! their destructors don't run. This is because `tempfile` relies on the OS to cleanup the -//! underlying file, while `TempDir` and `NamedTempFile` rely on their destructors to do so. +//! `tempfile` will (almost) never fail to cleanup temporary resources. However `TempDir` and `NamedTempFile` will +//! fail if their destructors don't run. This is because `tempfile` relies on the OS to cleanup the +//! underlying file, while `TempDir` and `NamedTempFile` rely on rust destructors to do so. +//! Destructors may fail to run if the process exits through an unhandled signal interrupt (like `SIGINT`), +//! or if the instance is declared statically (like with [`lazy_static`]), among other possible +//! reasons. //! //! ## Security //! @@ -152,6 +155,7 @@ //! [`TempDir`]: struct.TempDir.html //! [`NamedTempFile`]: struct.NamedTempFile.html //! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html +//! [`lazy_static`]: https://github.com/rust-lang-nursery/lazy-static.rs/issues/62 #![doc( html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", @@ -161,7 +165,7 @@ #![cfg_attr(test, deny(warnings))] #![deny(rust_2018_idioms)] #![allow(clippy::redundant_field_names)] -#![cfg_attr(feature = "nightly", feature(wasi_ext))] +#![cfg_attr(all(feature = "nightly", target_os = "wasi"), feature(wasi_ext))] #[cfg(doctest)] doc_comment::doctest!("../README.md"); @@ -276,6 +280,15 @@ impl<'a, 'b> Builder<'a, 'b> { /// # Ok(()) /// # } /// ``` + /// + /// Create a temporary directory with a chosen prefix under a chosen folder: + /// + /// ```ignore + /// let dir = Builder::new() + /// .prefix("my-temporary-dir") + /// .tempdir_in("folder-with-tempdirs")?; + /// ``` + #[must_use] pub fn new() -> Self { Self::default() } @@ -419,7 +432,7 @@ impl<'a, 'b> Builder<'a, 'b> { /// [security]: struct.NamedTempFile.html#security /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking pub fn tempfile(&self) -> io::Result<NamedTempFile> { - self.tempfile_in(&env::temp_dir()) + self.tempfile_in(env::temp_dir()) } /// Create the named temporary file in the specified directory. @@ -493,7 +506,7 @@ impl<'a, 'b> Builder<'a, 'b> { /// /// [resource-leaking]: struct.TempDir.html#resource-leaking pub fn tempdir(&self) -> io::Result<TempDir> { - self.tempdir_in(&env::temp_dir()) + self.tempdir_in(env::temp_dir()) } /// Attempts to make a temporary directory inside of `dir`. @@ -534,4 +547,155 @@ impl<'a, 'b> Builder<'a, 'b> { util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create) } + + /// Attempts to create a temporary file (or file-like object) using the + /// provided closure. The closure is passed a temporary file path and + /// returns an [`std::io::Result`]. The path provided to the closure will be + /// inside of [`std::env::temp_dir()`]. Use [`Builder::make_in`] to provide + /// a custom temporary directory. If the closure returns one of the + /// following errors, then another randomized file path is tried: + /// - [`std::io::ErrorKind::AlreadyExists`] + /// - [`std::io::ErrorKind::AddrInUse`] + /// + /// This can be helpful for taking full control over the file creation, but + /// leaving the temporary file path construction up to the library. This + /// also enables creating a temporary UNIX domain socket, since it is not + /// possible to bind to a socket that already exists. + /// + /// Note that [`Builder::append`] is ignored when using [`Builder::make`]. + /// + /// # Security + /// + /// This has the same [security implications][security] as + /// [`NamedTempFile`], but with additional caveats. Specifically, it is up + /// to the closure to ensure that the file does not exist and that such a + /// check is *atomic*. Otherwise, a [time-of-check to time-of-use + /// bug][TOCTOU] could be introduced. + /// + /// For example, the following is **not** secure: + /// + /// ``` + /// # use std::io; + /// # use std::fs::File; + /// # fn main() { + /// # if let Err(_) = run() { + /// # ::std::process::exit(1); + /// # } + /// # } + /// # fn run() -> Result<(), io::Error> { + /// # use tempfile::Builder; + /// // This is NOT secure! + /// let tempfile = Builder::new().make(|path| { + /// if path.is_file() { + /// return Err(io::ErrorKind::AlreadyExists.into()); + /// } + /// + /// // Between the check above and the usage below, an attacker could + /// // have replaced `path` with another file, which would get truncated + /// // by `File::create`. + /// + /// File::create(path) + /// })?; + /// # Ok(()) + /// # } + /// ``` + /// Note that simply using [`std::fs::File::create`] alone is not correct + /// because it does not fail if the file already exists: + /// ``` + /// # use std::io; + /// # use std::fs::File; + /// # fn main() { + /// # if let Err(_) = run() { + /// # ::std::process::exit(1); + /// # } + /// # } + /// # fn run() -> Result<(), io::Error> { + /// # use tempfile::Builder; + /// // This could overwrite an existing file! + /// let tempfile = Builder::new().make(|path| File::create(path))?; + /// # Ok(()) + /// # } + /// ``` + /// For creating regular temporary files, use [`Builder::tempfile`] instead + /// to avoid these problems. This function is meant to enable more exotic + /// use-cases. + /// + /// # Resource leaking + /// + /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`. + /// + /// # Errors + /// + /// If the closure returns any error besides + /// [`std::io::ErrorKind::AlreadyExists`] or + /// [`std::io::ErrorKind::AddrInUse`], then `Err` is returned. + /// + /// # Examples + /// ``` + /// # use std::io; + /// # fn main() { + /// # if let Err(_) = run() { + /// # ::std::process::exit(1); + /// # } + /// # } + /// # fn run() -> Result<(), io::Error> { + /// # use tempfile::Builder; + /// # #[cfg(unix)] + /// use std::os::unix::net::UnixListener; + /// # #[cfg(unix)] + /// let tempsock = Builder::new().make(|path| UnixListener::bind(path))?; + /// # Ok(()) + /// # } + /// ``` + /// + /// [TOCTOU]: https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use + /// [security]: struct.NamedTempFile.html#security + /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking + pub fn make<F, R>(&self, f: F) -> io::Result<NamedTempFile<R>> + where + F: FnMut(&Path) -> io::Result<R>, + { + self.make_in(env::temp_dir(), f) + } + + /// This is the same as [`Builder::make`], except `dir` is used as the base + /// directory for the temporary file path. + /// + /// See [`Builder::make`] for more details and security implications. + /// + /// # Examples + /// ``` + /// # use std::io; + /// # fn main() { + /// # if let Err(_) = run() { + /// # ::std::process::exit(1); + /// # } + /// # } + /// # fn run() -> Result<(), io::Error> { + /// # use tempfile::Builder; + /// # #[cfg(unix)] + /// use std::os::unix::net::UnixListener; + /// # #[cfg(unix)] + /// let tempsock = Builder::new().make_in("./", |path| UnixListener::bind(path))?; + /// # Ok(()) + /// # } + /// ``` + pub fn make_in<F, R, P>(&self, dir: P, mut f: F) -> io::Result<NamedTempFile<R>> + where + F: FnMut(&Path) -> io::Result<R>, + P: AsRef<Path>, + { + util::create_helper( + dir.as_ref(), + self.prefix, + self.suffix, + self.random_len, + move |path| { + Ok(NamedTempFile::from_parts( + f(&path)?, + TempPath::from_path(path), + )) + }, + ) + } } diff --git a/vendor/tempfile/src/spooled.rs b/vendor/tempfile/src/spooled.rs index ed6c16fb4..db14967ca 100644 --- a/vendor/tempfile/src/spooled.rs +++ b/vendor/tempfile/src/spooled.rs @@ -64,14 +64,16 @@ pub fn spooled_tempfile(max_size: usize) -> SpooledTempFile { } impl SpooledTempFile { + #[must_use] pub fn new(max_size: usize) -> SpooledTempFile { SpooledTempFile { - max_size: max_size, + max_size, inner: SpooledData::InMemory(Cursor::new(Vec::new())), } } /// Returns true if the file has been rolled over to disk. + #[must_use] pub fn is_rolled(&self) -> bool { match self.inner { SpooledData::InMemory(_) => false, @@ -107,6 +109,7 @@ impl SpooledTempFile { } /// Consumes and returns the inner `SpooledData` type. + #[must_use] pub fn into_inner(self) -> SpooledData { self.inner } diff --git a/vendor/tempfile/src/util.rs b/vendor/tempfile/src/util.rs index 8c91b9c69..c61082d50 100644 --- a/vendor/tempfile/src/util.rs +++ b/vendor/tempfile/src/util.rs @@ -1,4 +1,3 @@ -use fastrand; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; use std::{io, iter::repeat_with}; @@ -21,10 +20,10 @@ pub fn create_helper<F, R>( prefix: &OsStr, suffix: &OsStr, random_len: usize, - f: F, + mut f: F, ) -> io::Result<R> where - F: Fn(PathBuf) -> io::Result<R>, + F: FnMut(PathBuf) -> io::Result<R>, { let num_retries = if random_len != 0 { crate::NUM_RETRIES @@ -35,7 +34,10 @@ where for _ in 0..num_retries { let path = base.join(tmpname(prefix, suffix, random_len)); return match f(path) { - Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => continue, + Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists && num_retries > 1 => continue, + // AddrInUse can happen if we're creating a UNIX domain socket and + // the path already exists. + Err(ref e) if e.kind() == io::ErrorKind::AddrInUse && num_retries > 1 => continue, res => res, }; } diff --git a/vendor/tempfile/tests/namedtempfile.rs b/vendor/tempfile/tests/namedtempfile.rs index d2c7da22b..54396c3ac 100644 --- a/vendor/tempfile/tests/namedtempfile.rs +++ b/vendor/tempfile/tests/namedtempfile.rs @@ -87,7 +87,7 @@ fn test_persist_noclobber() { fn test_customnamed() { let tmpfile = Builder::new() .prefix("tmp") - .suffix(&".rs".to_string()) + .suffix(&".rs") .rand_bytes(12) .tempfile() .unwrap(); @@ -100,9 +100,9 @@ fn test_customnamed() { #[test] fn test_append() { let mut tmpfile = Builder::new().append(true).tempfile().unwrap(); - tmpfile.write(b"a").unwrap(); + tmpfile.write_all(b"a").unwrap(); tmpfile.seek(SeekFrom::Start(0)).unwrap(); - tmpfile.write(b"b").unwrap(); + tmpfile.write_all(b"b").unwrap(); tmpfile.seek(SeekFrom::Start(0)).unwrap(); let mut buf = vec![0u8; 1]; @@ -299,6 +299,18 @@ fn test_into_parts() { } #[test] +fn test_from_parts() { + let mut file = NamedTempFile::new().unwrap(); + write!(file, "abcd").expect("write failed"); + + let (file, temp_path) = file.into_parts(); + + let file = NamedTempFile::from_parts(file, temp_path); + + assert!(file.path().exists()); +} + +#[test] fn test_keep() { let mut tmpfile = NamedTempFile::new().unwrap(); write!(tmpfile, "abcde").unwrap(); @@ -326,3 +338,129 @@ fn test_keep() { } std::fs::remove_file(&path).unwrap(); } + +#[test] +fn test_make() { + let tmpfile = Builder::new().make(|path| File::create(path)).unwrap(); + + assert!(tmpfile.path().is_file()); +} + +#[test] +fn test_make_in() { + let tmp_dir = tempdir().unwrap(); + + let tmpfile = Builder::new() + .make_in(tmp_dir.path(), |path| File::create(path)) + .unwrap(); + + assert!(tmpfile.path().is_file()); + assert_eq!(tmpfile.path().parent(), Some(tmp_dir.path())); +} + +#[test] +fn test_make_fnmut() { + let mut count = 0; + + // Show that an FnMut can be used. + let tmpfile = Builder::new() + .make(|path| { + count += 1; + File::create(path) + }) + .unwrap(); + + assert!(tmpfile.path().is_file()); +} + +#[cfg(unix)] +#[test] +fn test_make_uds() { + use std::os::unix::net::UnixListener; + + let temp_sock = Builder::new() + .prefix("tmp") + .suffix(".sock") + .rand_bytes(12) + .make(|path| UnixListener::bind(path)) + .unwrap(); + + assert!(temp_sock.path().exists()); +} + +#[cfg(unix)] +#[test] +fn test_make_uds_conflict() { + use std::os::unix::net::UnixListener; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + + // Check that retries happen correctly by racing N different threads. + + const NTHREADS: usize = 20; + + // The number of times our callback was called. + let tries = Arc::new(AtomicUsize::new(0)); + + let mut threads = Vec::with_capacity(NTHREADS); + + for _ in 0..NTHREADS { + let tries = tries.clone(); + threads.push(std::thread::spawn(move || { + // Ensure that every thread uses the same seed so we are guaranteed + // to retry. Note that fastrand seeds are thread-local. + fastrand::seed(42); + + Builder::new() + .prefix("tmp") + .suffix(".sock") + .rand_bytes(12) + .make(|path| { + tries.fetch_add(1, Ordering::Relaxed); + UnixListener::bind(path) + }) + })); + } + + // Join all threads, but don't drop the temp file yet. Otherwise, we won't + // get a deterministic number of `tries`. + let sockets: Vec<_> = threads + .into_iter() + .map(|thread| thread.join().unwrap().unwrap()) + .collect(); + + // Number of tries is exactly equal to (n*(n+1))/2. + assert_eq!( + tries.load(Ordering::Relaxed), + (NTHREADS * (NTHREADS + 1)) / 2 + ); + + for socket in sockets { + assert!(socket.path().exists()); + } +} + +// Issue #224. +#[test] +fn test_overly_generic_bounds() { + pub struct Foo<T>(T); + + impl<T> Foo<T> + where + T: Sync + Send + 'static, + for<'a> &'a T: Write + Read, + { + pub fn new(foo: T) -> Self { + Self(foo) + } + } + + // Don't really need to run this. Only care if it compiles. + if let Ok(file) = File::open("i_do_not_exist") { + let mut f; + let _x = { + f = Foo::new(file); + &mut f + }; + } +} diff --git a/vendor/tempfile/tests/tempdir.rs b/vendor/tempfile/tests/tempdir.rs index 746fe4738..de9723328 100644 --- a/vendor/tempfile/tests/tempdir.rs +++ b/vendor/tempfile/tests/tempdir.rs @@ -67,7 +67,7 @@ fn test_customnamed() { fn test_rm_tempdir() { let (tx, rx) = channel(); - let f = move || -> () { + let f = move || { let tmp = t!(TempDir::new()); tx.send(tmp.path().to_path_buf()).unwrap(); panic!("panic to unwind past `tmp`"); @@ -78,7 +78,7 @@ fn test_rm_tempdir() { let tmp = t!(TempDir::new()); let path = tmp.path().to_path_buf(); - let f = move || -> () { + let f = move || { let _tmp = tmp; panic!("panic to unwind past `tmp`"); }; @@ -107,7 +107,7 @@ fn test_rm_tempdir() { fn test_rm_tempdir_close() { let (tx, rx) = channel(); - let f = move || -> () { + let f = move || { let tmp = t!(TempDir::new()); tx.send(tmp.path().to_path_buf()).unwrap(); t!(tmp.close()); @@ -119,7 +119,7 @@ fn test_rm_tempdir_close() { let tmp = t!(TempDir::new()); let path = tmp.path().to_path_buf(); - let f = move || -> () { + let f = move || { let tmp = tmp; t!(tmp.close()); panic!("panic when unwinding past `tmp`"); diff --git a/vendor/tempfile/tests/tempfile.rs b/vendor/tempfile/tests/tempfile.rs index f4dddb290..c2f684438 100644 --- a/vendor/tempfile/tests/tempfile.rs +++ b/vendor/tempfile/tests/tempfile.rs @@ -2,8 +2,11 @@ use std::fs; use std::io::{Read, Seek, SeekFrom, Write}; -use std::sync::mpsc::{sync_channel, TryRecvError}; -use std::thread; +#[cfg(target_os = "linux")] +use std::{ + sync::mpsc::{sync_channel, TryRecvError}, + thread, +}; #[test] fn test_basic() { |