RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.

This commit is contained in:
Adam Ierymenko
2022-06-08 07:32:16 -04:00
parent 373ca30269
commit d5ca4e5f52
12611 changed files with 2898014 additions and 284 deletions

415
zeroidc/vendor/tempfile/src/dir.rs vendored Normal file
View File

@@ -0,0 +1,415 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// 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};
use crate::error::IoResultExt;
use crate::Builder;
/// Create a new temporary directory.
///
/// The `tempdir` function creates a directory in the file system
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # 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 dir = tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir() -> io::Result<TempDir> {
TempDir::new()
}
/// Create a new temporary directory.
///
/// The `tempdir` function creates a directory in the file system
/// and returns a [`TempDir`].
/// The directory will be automatically deleted when the `TempDir`s
/// destructor is run.
///
/// # Resource Leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempdir;
/// use std::fs::File;
/// use std::io::{self, Write};
///
/// # 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 dir = tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
///
/// [`TempDir`]: struct.TempDir.html
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
TempDir::new_in(dir)
}
/// A directory in the filesystem that is automatically deleted when
/// it goes out of scope.
///
/// The [`TempDir`] type creates a directory on the file system that
/// is deleted once it goes out of scope. At construction, the
/// `TempDir` creates a new directory with a randomly generated name.
///
/// The default constructor, [`TempDir::new()`], creates directories in
/// the location returned by [`std::env::temp_dir()`], but `TempDir`
/// can be configured to manage a temporary directory in any location
/// by constructing with a [`Builder`].
///
/// After creating a `TempDir`, work with the file system by doing
/// standard [`std::fs`] file system operations on its [`Path`],
/// which can be retrieved with [`TempDir::path()`]. Once the `TempDir`
/// value is dropped, the directory at the path will be deleted, along
/// with any files and directories it contains. It is your responsibility
/// to ensure that no further file system operations are attempted
/// inside the temporary directory once it has been deleted.
///
/// # Resource Leaking
///
/// Various platform-specific conditions may cause `TempDir` to fail
/// to delete the underlying directory. It's important to ensure that
/// handles (like [`File`] and [`ReadDir`]) to files inside the
/// directory are dropped before the `TempDir` goes out of scope. The
/// `TempDir` destructor will silently ignore any errors in deleting
/// the directory; to instead handle errors call [`TempDir::close()`].
///
/// Note that if the program exits before the `TempDir` destructor is
/// run, such as via [`std::process::exit()`], by segfaulting, or by
/// receiving a signal like `SIGINT`, then the temporary directory
/// will not be deleted.
///
/// # Examples
///
/// Create a temporary directory with a generated name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let tmp_dir = TempDir::new()?;
/// # Ok(())
/// # }
/// ```
///
/// Create a temporary directory with a prefix in its name:
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::Builder;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`,
/// // whose name will begin with 'example'.
/// let tmp_dir = Builder::new().prefix("example").tempdir()?;
/// # Ok(())
/// # }
/// ```
///
/// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html
/// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
/// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html
/// [`Builder`]: struct.Builder.html
/// [`TempDir::close()`]: struct.TempDir.html#method.close
/// [`TempDir::new()`]: struct.TempDir.html#method.new
/// [`TempDir::path()`]: struct.TempDir.html#method.path
/// [`TempDir`]: struct.TempDir.html
/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
/// [`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: Box<Path>,
}
impl TempDir {
/// Attempts to make a temporary directory inside of `env::temp_dir()`.
///
/// See [`Builder`] for more configuration.
///
/// The directory and everything inside it will be automatically deleted
/// once the returned `TempDir` is destroyed.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`
/// let tmp_dir = TempDir::new()?;
///
/// let file_path = tmp_dir.path().join("my-temporary-note.txt");
/// let mut tmp_file = File::create(file_path)?;
/// writeln!(tmp_file, "Brian was here. Briefly.")?;
///
/// // `tmp_dir` goes out of scope, the directory as well as
/// // `tmp_file` will be deleted here.
/// # Ok(())
/// # }
/// ```
///
/// [`Builder`]: struct.Builder.html
pub fn new() -> io::Result<TempDir> {
Builder::new().tempdir()
}
/// Attempts to make a temporary directory inside of `dir`.
/// The directory and everything inside it will be automatically
/// deleted once the returned `TempDir` is destroyed.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use std::fs::{self, File};
/// use std::io::Write;
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of the current directory
/// let tmp_dir = TempDir::new_in(".")?;
/// let file_path = tmp_dir.path().join("my-temporary-note.txt");
/// let mut tmp_file = File::create(file_path)?;
/// writeln!(tmp_file, "Brian was here. Briefly.")?;
/// # Ok(())
/// # }
/// ```
pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
Builder::new().tempdir_in(dir)
}
/// Accesses the [`Path`] to the temporary directory.
///
/// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
///
/// # Examples
///
/// ```
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// let tmp_path;
///
/// {
/// let tmp_dir = TempDir::new()?;
/// tmp_path = tmp_dir.path().to_owned();
///
/// // Check that the temp directory actually exists.
/// assert!(tmp_path.exists());
///
/// // End of `tmp_dir` scope, directory will be deleted
/// }
///
/// // Temp directory should be deleted by now
/// assert_eq!(tmp_path.exists(), false);
/// # Ok(())
/// # }
/// ```
pub fn path(&self) -> &path::Path {
self.path.as_ref()
}
/// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
///
/// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
/// the directory will no longer be automatically deleted.
///
/// [`TempDir`]: struct.TempDir.html
/// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
///
/// # Examples
///
/// ```
/// use std::fs;
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// let tmp_dir = TempDir::new()?;
///
/// // Persist the temporary directory to disk,
/// // getting the path where it is.
/// let tmp_path = tmp_dir.into_path();
///
/// // Delete the temporary directory ourselves.
/// fs::remove_dir_all(tmp_path)?;
/// # Ok(())
/// # }
/// ```
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`.
///
/// Although `TempDir` removes the directory on drop, in the destructor
/// any errors are ignored. To detect errors cleaning up the temporary
/// directory, call `close` instead.
///
/// # Errors
///
/// This function may return a variety of [`std::io::Error`]s that result from deleting
/// the files and directories contained with the temporary directory,
/// as well as from deleting the temporary directory itself. These errors
/// may be platform specific.
///
/// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
///
/// # Examples
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::TempDir;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// // Create a directory inside of `std::env::temp_dir()`.
/// let tmp_dir = TempDir::new()?;
/// let file_path = tmp_dir.path().join("my-temporary-note.txt");
/// let mut tmp_file = File::create(file_path)?;
/// writeln!(tmp_file, "Brian was here. Briefly.")?;
///
/// // By closing the `TempDir` explicitly we can check that it has
/// // been deleted successfully. If we don't close it explicitly,
/// // the directory will still be deleted when `tmp_dir` goes out
/// // of scope, but we won't know whether deleting the directory
/// // succeeded.
/// drop(tmp_file);
/// tmp_dir.close()?;
/// # Ok(())
/// # }
/// ```
pub fn close(mut self) -> io::Result<()> {
let result = remove_dir_all(self.path()).with_err_path(|| self.path());
// 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
}
}
impl AsRef<Path> for TempDir {
fn as_ref(&self) -> &Path {
self.path()
}
}
impl fmt::Debug for TempDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TempDir")
.field("path", &self.path())
.finish()
}
}
impl Drop for TempDir {
fn drop(&mut self) {
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: path.into_boxed_path(),
})
}

45
zeroidc/vendor/tempfile/src/error.rs vendored Normal file
View File

@@ -0,0 +1,45 @@
use std::path::PathBuf;
use std::{error, fmt, io};
#[derive(Debug)]
struct PathError {
path: PathBuf,
err: io::Error,
}
impl fmt::Display for PathError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} at path {:?}", self.err, self.path)
}
}
impl error::Error for PathError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.err.source()
}
}
pub(crate) trait IoResultExt<T> {
fn with_err_path<F, P>(self, path: F) -> Self
where
F: FnOnce() -> P,
P: Into<PathBuf>;
}
impl<T> IoResultExt<T> for Result<T, io::Error> {
fn with_err_path<F, P>(self, path: F) -> Self
where
F: FnOnce() -> P,
P: Into<PathBuf>,
{
self.map_err(|e| {
io::Error::new(
e.kind(),
PathError {
path: path().into(),
err: e,
},
)
})
}
}

View File

@@ -0,0 +1,12 @@
cfg_if::cfg_if! {
if #[cfg(any(unix, target_os = "redox", target_os = "wasi"))] {
mod unix;
pub use self::unix::*;
} else if #[cfg(windows)] {
mod windows;
pub use self::windows::*;
} else {
mod other;
pub use self::other::*;
}
}

View File

@@ -0,0 +1,30 @@
use std::fs::{File, OpenOptions};
use std::io;
use std::path::Path;
fn not_supported<T>() -> io::Result<T> {
Err(io::Error::new(
io::ErrorKind::Other,
"operation not supported on this platform",
))
}
pub fn create_named(_path: &Path, open_options: &mut OpenOptions) -> io::Result<File> {
not_supported()
}
pub fn create(_dir: &Path) -> io::Result<File> {
not_supported()
}
pub fn reopen(_file: &File, _path: &Path) -> io::Result<File> {
not_supported()
}
pub fn persist(_old_path: &Path, _new_path: &Path, _overwrite: bool) -> io::Result<()> {
not_supported()
}
pub fn keep(path: &Path) -> io::Result<()> {
not_supported()
}

View File

@@ -0,0 +1,156 @@
use std::env;
use std::ffi::{CString, 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;
}
}
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"))
}
pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> {
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> {
let tmp;
// shadow this to decrease the lifetime. It can't live longer than `tmp`.
let mut path = path;
if !path.is_absolute() {
let cur_dir = env::current_dir()?;
tmp = cur_dir.join(path);
path = &tmp;
}
let f = create_named(path, &mut OpenOptions::new())?;
// don't care whether the path has already been unlinked,
// but perhaps there are some IO error conditions we should send up?
let _ = fs::remove_file(path);
Ok(f)
}
#[cfg(target_os = "linux")]
pub fn create(dir: &Path) -> io::Result<File> {
use libc::{EISDIR, ENOENT, EOPNOTSUPP, O_TMPFILE};
OpenOptions::new()
.read(true)
.write(true)
.custom_flags(O_TMPFILE) // do not mix with `create_new(true)`
.open(dir)
.or_else(|e| {
match e.raw_os_error() {
// These are the three "not supported" error codes for O_TMPFILE.
Some(EOPNOTSUPP) | Some(EISDIR) | Some(ENOENT) => create_unix(dir),
_ => Err(e),
}
})
}
#[cfg(not(target_os = "linux"))]
pub fn create(dir: &Path) -> io::Result<File> {
create_unix(dir)
}
fn create_unix(dir: &Path) -> io::Result<File> {
util::create_helper(
dir,
OsStr::new(".tmp"),
OsStr::new(""),
crate::NUM_RAND_CHARS,
|path| create_unlinked(&path),
)
}
#[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()?;
let new_meta = new_file.metadata()?;
if old_meta.dev() != new_meta.dev() || old_meta.ino() != new_meta.ino() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"original tempfile has been replaced",
));
}
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 {
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);
}
Ok(())
}
}
#[cfg(target_os = "redox")]
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))
}
pub fn keep(_: &Path) -> io::Result<()> {
Ok(())
}

View File

@@ -0,0 +1,108 @@
use std::ffi::OsStr;
use std::fs::{File, OpenOptions};
use std::os::windows::ffi::OsStrExt;
use std::os::windows::fs::OpenOptionsExt;
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 crate::util;
fn to_utf16(s: &Path) -> Vec<u16> {
s.as_os_str().encode_wide().chain(iter::once(0)).collect()
}
pub fn create_named(path: &Path, open_options: &mut OpenOptions) -> io::Result<File> {
open_options
.create_new(true)
.read(true)
.write(true)
.custom_flags(FILE_ATTRIBUTE_TEMPORARY)
.open(path)
}
pub fn create(dir: &Path) -> io::Result<File> {
util::create_helper(
dir,
OsStr::new(".tmp"),
OsStr::new(""),
crate::NUM_RAND_CHARS,
|path| {
OpenOptions::new()
.create_new(true)
.read(true)
.write(true)
.share_mode(0)
.custom_flags(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE)
.open(path)
},
)
}
pub fn reopen(file: &File, _path: &Path) -> io::Result<File> {
let handle = file.as_raw_handle();
unsafe {
let handle = ReOpenFile(
handle as HANDLE,
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
);
if handle == INVALID_HANDLE_VALUE {
Err(io::Error::last_os_error())
} else {
Ok(FromRawHandle::from_raw_handle(handle as RawHandle))
}
}
}
pub fn keep(path: &Path) -> io::Result<()> {
unsafe {
let path_w = to_utf16(path);
if SetFileAttributesW(path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
}
pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> {
// TODO: We should probably do this in one-shot using SetFileInformationByHandle but the API is
// really painful.
unsafe {
let old_path_w = to_utf16(old_path);
let new_path_w = to_utf16(new_path);
// Don't succeed if this fails. We don't want to claim to have successfully persisted a file
// still marked as temporary because this file won't have the same consistency guarantees.
if SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 {
return Err(io::Error::last_os_error());
}
let mut flags = 0;
if overwrite {
flags |= MOVEFILE_REPLACE_EXISTING;
}
if MoveFileExW(old_path_w.as_ptr(), new_path_w.as_ptr(), flags) == 0 {
let e = io::Error::last_os_error();
// If this fails, the temporary file is now un-hidden and no longer marked temporary
// (slightly less efficient) but it will still work.
let _ = SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_TEMPORARY);
Err(e)
} else {
Ok(())
}
}
}

973
zeroidc/vendor/tempfile/src/file/mod.rs vendored Normal file
View File

@@ -0,0 +1,973 @@
use std::env;
use std::error;
use std::ffi::OsStr;
use std::fmt;
use std::fs::{self, File, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use crate::error::IoResultExt;
use crate::Builder;
mod imp;
/// Create a new temporary file.
///
/// The file will be created in the location returned by [`std::env::temp_dir()`].
///
/// # Security
///
/// This variant is secure/reliable in the presence of a pathological temporary file cleaner.
///
/// # Resource Leaking
///
/// The temporary file will be automatically removed by the OS when the last handle to it is closed.
/// This doesn't rely on Rust destructors being run, so will (almost) never fail to clean up the temporary file.
///
/// # Errors
///
/// If the file can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempfile;
/// use std::io::{self, Write};
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a file inside of `std::env::temp_dir()`.
/// let mut file = tempfile()?;
///
/// writeln!(file, "Brian was here. Briefly.")?;
/// # Ok(())
/// # }
/// ```
///
/// [`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())
}
/// Create a new temporary file in the specified directory.
///
/// # Security
///
/// This variant is secure/reliable in the presence of a pathological temporary file cleaner.
/// If the temporary file isn't created in [`std::env::temp_dir()`] then temporary file cleaners aren't an issue.
///
/// # Resource Leaking
///
/// The temporary file will be automatically removed by the OS when the last handle to it is closed.
/// This doesn't rely on Rust destructors being run, so will (almost) never fail to clean up the temporary file.
///
/// # Errors
///
/// If the file can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use tempfile::tempfile_in;
/// use std::io::{self, Write};
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// // Create a file inside of the current working directory
/// let mut file = tempfile_in("./")?;
///
/// writeln!(file, "Brian was here. Briefly.")?;
/// # Ok(())
/// # }
/// ```
///
/// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
pub fn tempfile_in<P: AsRef<Path>>(dir: P) -> io::Result<File> {
imp::create(dir.as_ref())
}
/// Error returned when persisting a temporary file path fails.
#[derive(Debug)]
pub struct PathPersistError {
/// The underlying IO error.
pub error: io::Error,
/// The temporary file path that couldn't be persisted.
pub path: TempPath,
}
impl From<PathPersistError> for io::Error {
#[inline]
fn from(error: PathPersistError) -> io::Error {
error.error
}
}
impl From<PathPersistError> for TempPath {
#[inline]
fn from(error: PathPersistError) -> TempPath {
error.path
}
}
impl fmt::Display for PathPersistError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "failed to persist temporary file path: {}", self.error)
}
}
impl error::Error for PathPersistError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.error)
}
}
/// A path to a named temporary file without an open file handle.
///
/// This is useful when the temporary file needs to be used by a child process,
/// for example.
///
/// When dropped, the temporary file is deleted.
pub struct TempPath {
path: Box<Path>,
}
impl TempPath {
/// Close and remove the temporary file.
///
/// Use this if you want to detect errors in deleting the file.
///
/// # Errors
///
/// If the file cannot be deleted, `Err` is returned.
///
/// # Examples
///
/// ```no_run
/// # use std::io;
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let file = NamedTempFile::new()?;
///
/// // Close the file, but keep the path to it around.
/// let path = file.into_temp_path();
///
/// // By closing the `TempPath` explicitly, we can check that it has
/// // been deleted successfully. If we don't close it explicitly, the
/// // file will still be deleted when `file` goes out of scope, but we
/// // won't know whether deleting the file succeeded.
/// path.close()?;
/// # Ok(())
/// # }
/// ```
pub fn close(mut self) -> io::Result<()> {
let result = fs::remove_file(&self.path).with_err_path(|| &*self.path);
self.path = PathBuf::new().into_boxed_path();
mem::forget(self);
result
}
/// Persist the temporary file at the target path.
///
/// If a file exists at the target path, persist will atomically replace it.
/// If this method fails, it will return `self` in the resulting
/// [`PathPersistError`].
///
/// Note: Temporary files cannot be persisted across filesystems. Also
/// neither the file contents nor the containing directory are
/// synchronized, so the update may not yet have reached the disk when
/// `persist` returns.
///
/// # Security
///
/// Only use this method if you're positive that a temporary file cleaner
/// won't have deleted your file. Otherwise, you might end up persisting an
/// attacker controlled file.
///
/// # Errors
///
/// If the file cannot be moved to the new location, `Err` is returned.
///
/// # Examples
///
/// ```no_run
/// # use std::io::{self, Write};
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let mut file = NamedTempFile::new()?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// let path = file.into_temp_path();
/// path.persist("./saved_file.txt")?;
/// # Ok(())
/// # }
/// ```
///
/// [`PathPersistError`]: struct.PathPersistError.html
pub fn persist<P: AsRef<Path>>(mut self, new_path: P) -> Result<(), PathPersistError> {
match imp::persist(&self.path, new_path.as_ref(), true) {
Ok(_) => {
// 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().into_boxed_path();
mem::forget(self);
Ok(())
}
Err(e) => Err(PathPersistError {
error: e,
path: self,
}),
}
}
/// Persist the temporary file at the target path if and only if no file exists there.
///
/// If a file exists at the target path, fail. If this method fails, it will
/// return `self` in the resulting [`PathPersistError`].
///
/// Note: Temporary files cannot be persisted across filesystems. Also Note:
/// This method is not atomic. It can leave the original link to the
/// temporary file behind.
///
/// # Security
///
/// Only use this method if you're positive that a temporary file cleaner
/// won't have deleted your file. Otherwise, you might end up persisting an
/// attacker controlled file.
///
/// # Errors
///
/// If the file cannot be moved to the new location or a file already exists
/// there, `Err` is returned.
///
/// # Examples
///
/// ```no_run
/// # use std::io::{self, Write};
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let mut file = NamedTempFile::new()?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// let path = file.into_temp_path();
/// path.persist_noclobber("./saved_file.txt")?;
/// # Ok(())
/// # }
/// ```
///
/// [`PathPersistError`]: struct.PathPersistError.html
pub fn persist_noclobber<P: AsRef<Path>>(
mut self,
new_path: P,
) -> Result<(), PathPersistError> {
match imp::persist(&self.path, new_path.as_ref(), false) {
Ok(_) => {
// 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().into_boxed_path();
mem::forget(self);
Ok(())
}
Err(e) => Err(PathPersistError {
error: e,
path: self,
}),
}
}
/// Keep the temporary file from being deleted. This function will turn the
/// temporary file into a non-temporary file without moving it.
///
///
/// # Errors
///
/// On some platforms (e.g., Windows), we need to mark the file as
/// non-temporary. This operation could fail.
///
/// # Examples
///
/// ```no_run
/// # use std::io::{self, Write};
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let mut file = NamedTempFile::new()?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// let path = file.into_temp_path();
/// let path = path.keep()?;
/// # Ok(())
/// # }
/// ```
///
/// [`PathPersistError`]: struct.PathPersistError.html
pub fn keep(mut self) -> Result<PathBuf, PathPersistError> {
match imp::keep(&self.path) {
Ok(_) => {
// 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().into_boxed_path());
mem::forget(self);
Ok(path.into())
}
Err(e) => Err(PathPersistError {
error: e,
path: self,
}),
}
}
/// 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 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.path.fmt(f)
}
}
impl Drop for TempPath {
fn drop(&mut self) {
let _ = fs::remove_file(&self.path);
}
}
impl Deref for TempPath {
type Target = Path;
fn deref(&self) -> &Path {
&self.path
}
}
impl AsRef<Path> for TempPath {
fn as_ref(&self) -> &Path {
&self.path
}
}
impl AsRef<OsStr> for TempPath {
fn as_ref(&self) -> &OsStr {
self.path.as_os_str()
}
}
/// A named temporary file.
///
/// The default constructor, [`NamedTempFile::new()`], creates files in
/// the location returned by [`std::env::temp_dir()`], but `NamedTempFile`
/// can be configured to manage a temporary file in any location
/// by constructing with [`NamedTempFile::new_in()`].
///
/// # Security
///
/// Most operating systems employ temporary file cleaners to delete old
/// temporary files. Unfortunately these temporary file cleaners don't always
/// reliably _detect_ whether the temporary file is still being used.
///
/// Specifically, the following sequence of events can happen:
///
/// 1. A user creates a temporary file with `NamedTempFile::new()`.
/// 2. Time passes.
/// 3. The temporary file cleaner deletes (unlinks) the temporary file from the
/// filesystem.
/// 4. Some other program creates a new file to replace this deleted temporary
/// file.
/// 5. The user tries to re-open the temporary file (in the same program or in a
/// different program) by path. Unfortunately, they'll end up opening the
/// file created by the other program, not the original file.
///
/// ## Operating System Specific Concerns
///
/// The behavior of temporary files and temporary file cleaners differ by
/// operating system.
///
/// ### Windows
///
/// On Windows, open files _can't_ be deleted. This removes most of the concerns
/// around temporary file cleaners.
///
/// Furthermore, temporary files are, by default, created in per-user temporary
/// file directories so only an application running as the same user would be
/// able to interfere (which they could do anyways). However, an application
/// running as the same user can still _accidentally_ re-create deleted
/// temporary files if the number of random bytes in the temporary file name is
/// too small.
///
/// So, the only real concern on Windows is:
///
/// 1. Opening a named temporary file in a world-writable directory.
/// 2. Using the `into_temp_path()` and/or `into_parts()` APIs to close the file
/// handle without deleting the underlying file.
/// 3. Continuing to use the file by path.
///
/// ### UNIX
///
/// Unlike on Windows, UNIX (and UNIX like) systems allow open files to be
/// "unlinked" (deleted).
///
/// #### MacOS
///
/// Like on Windows, temporary files are created in per-user temporary file
/// directories by default so calling `NamedTempFile::new()` should be
/// relatively safe.
///
/// #### Linux
///
/// Unfortunately, most _Linux_ distributions don't create per-user temporary
/// file directories. Worse, systemd's tmpfiles daemon (a common temporary file
/// cleaner) will happily remove open temporary files if they haven't been
/// modified within the last 10 days.
///
/// # 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.
///
/// Use the [`tempfile()`] function unless you absolutely need a named file.
///
/// [`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 {
path: TempPath,
file: File,
}
impl fmt::Debug for NamedTempFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NamedTempFile({:?})", self.path)
}
}
impl AsRef<Path> for NamedTempFile {
#[inline]
fn as_ref(&self) -> &Path {
self.path()
}
}
/// Error returned when persisting a temporary file fails.
#[derive(Debug)]
pub struct PersistError {
/// The underlying IO error.
pub error: io::Error,
/// The temporary file that couldn't be persisted.
pub file: NamedTempFile,
}
impl From<PersistError> for io::Error {
#[inline]
fn from(error: PersistError) -> io::Error {
error.error
}
}
impl From<PersistError> for NamedTempFile {
#[inline]
fn from(error: PersistError) -> NamedTempFile {
error.file
}
}
impl fmt::Display for PersistError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "failed to persist temporary file: {}", self.error)
}
}
impl error::Error for PersistError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.error)
}
}
impl NamedTempFile {
/// Create a new named temporary file.
///
/// See [`Builder`] for more configuration.
///
/// # Security
///
/// This will create a temporary file in the default temporary file
/// directory (platform dependent). This has security implications on many
/// platforms so please read the security section of this type's
/// documentation.
///
/// Reasons to use this method:
///
/// 1. The file has a short lifetime and your temporary file cleaner is
/// sane (doesn't delete recently accessed files).
///
/// 2. You trust every user on your system (i.e. you are the only user).
///
/// 3. You have disabled your system's temporary file cleaner or verified
/// that your system doesn't have a temporary file cleaner.
///
/// Reasons not to use this method:
///
/// 1. You'll fix it later. No you won't.
///
/// 2. You don't care about the security of the temporary file. If none of
/// the "reasons to use this method" apply, referring to a temporary
/// file by name may allow an attacker to create/overwrite your
/// non-temporary files. There are exceptions but if you don't already
/// know them, don't use this method.
///
/// # Errors
///
/// If the file can not be created, `Err` is returned.
///
/// # Examples
///
/// Create a named temporary file and write some data to it:
///
/// ```no_run
/// # use std::io::{self, Write};
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), ::std::io::Error> {
/// let mut file = NamedTempFile::new()?;
///
/// writeln!(file, "Brian was here. Briefly.")?;
/// # Ok(())
/// # }
/// ```
///
/// [`Builder`]: struct.Builder.html
pub fn new() -> io::Result<NamedTempFile> {
Builder::new().tempfile()
}
/// Create a new named temporary file in the specified directory.
///
/// See [`NamedTempFile::new()`] for details.
///
/// [`NamedTempFile::new()`]: #method.new
pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<NamedTempFile> {
Builder::new().tempfile_in(dir)
}
/// Get the temporary file's path.
///
/// # Security
///
/// Referring to a temporary file's path may not be secure in all cases.
/// Please read the security section on the top level documentation of this
/// type for details.
///
/// # Examples
///
/// ```no_run
/// # use std::io::{self, Write};
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), ::std::io::Error> {
/// let file = NamedTempFile::new()?;
///
/// println!("{:?}", file.path());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn path(&self) -> &Path {
&self.path
}
/// Close and remove the temporary file.
///
/// Use this if you want to detect errors in deleting the file.
///
/// # Errors
///
/// If the file cannot be deleted, `Err` is returned.
///
/// # Examples
///
/// ```no_run
/// # use std::io;
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let file = NamedTempFile::new()?;
///
/// // By closing the `NamedTempFile` explicitly, we can check that it has
/// // been deleted successfully. If we don't close it explicitly,
/// // the file will still be deleted when `file` goes out
/// // of scope, but we won't know whether deleting the file
/// // succeeded.
/// file.close()?;
/// # Ok(())
/// # }
/// ```
pub fn close(self) -> io::Result<()> {
let NamedTempFile { path, .. } = self;
path.close()
}
/// Persist the temporary file at the target path.
///
/// If a file exists at the target path, persist will atomically replace it.
/// If this method fails, it will return `self` in the resulting
/// [`PersistError`].
///
/// Note: Temporary files cannot be persisted across filesystems. Also
/// neither the file contents nor the containing directory are
/// synchronized, so the update may not yet have reached the disk when
/// `persist` returns.
///
/// # Security
///
/// This method persists the temporary file using its path and may not be
/// secure in the in all cases. Please read the security section on the top
/// level documentation of this type for details.
///
/// # Errors
///
/// If the file cannot be moved to the new location, `Err` is returned.
///
/// # Examples
///
/// ```no_run
/// # use std::io::{self, Write};
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let file = NamedTempFile::new()?;
///
/// let mut persisted_file = file.persist("./saved_file.txt")?;
/// writeln!(persisted_file, "Brian was here. Briefly.")?;
/// # Ok(())
/// # }
/// ```
///
/// [`PersistError`]: struct.PersistError.html
pub fn persist<P: AsRef<Path>>(self, new_path: P) -> Result<File, PersistError> {
let NamedTempFile { path, file } = self;
match path.persist(new_path) {
Ok(_) => Ok(file),
Err(err) => {
let PathPersistError { error, path } = err;
Err(PersistError {
file: NamedTempFile { path, file },
error,
})
}
}
}
/// Persist the temporary file at the target path if and only if no file exists there.
///
/// If a file exists at the target path, fail. If this method fails, it will
/// return `self` in the resulting PersistError.
///
/// Note: Temporary files cannot be persisted across filesystems. Also Note:
/// This method is not atomic. It can leave the original link to the
/// temporary file behind.
///
/// # Security
///
/// This method persists the temporary file using its path and may not be
/// secure in the in all cases. Please read the security section on the top
/// level documentation of this type for details.
///
/// # Errors
///
/// If the file cannot be moved to the new location or a file already exists there,
/// `Err` is returned.
///
/// # Examples
///
/// ```no_run
/// # use std::io::{self, Write};
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let file = NamedTempFile::new()?;
///
/// let mut persisted_file = file.persist_noclobber("./saved_file.txt")?;
/// writeln!(persisted_file, "Brian was here. Briefly.")?;
/// # Ok(())
/// # }
/// ```
pub fn persist_noclobber<P: AsRef<Path>>(self, new_path: P) -> Result<File, PersistError> {
let NamedTempFile { path, file } = self;
match path.persist_noclobber(new_path) {
Ok(_) => Ok(file),
Err(err) => {
let PathPersistError { error, path } = err;
Err(PersistError {
file: NamedTempFile { path, file },
error,
})
}
}
}
/// Keep the temporary file from being deleted. This function will turn the
/// temporary file into a non-temporary file without moving it.
///
///
/// # Errors
///
/// On some platforms (e.g., Windows), we need to mark the file as
/// non-temporary. This operation could fail.
///
/// # Examples
///
/// ```no_run
/// # use std::io::{self, Write};
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let mut file = NamedTempFile::new()?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// let (file, path) = file.keep()?;
/// # Ok(())
/// # }
/// ```
///
/// [`PathPersistError`]: struct.PathPersistError.html
pub fn keep(self) -> Result<(File, PathBuf), PersistError> {
let (file, path) = (self.file, self.path);
match path.keep() {
Ok(path) => Ok((file, path)),
Err(PathPersistError { error, path }) => Err(PersistError {
file: NamedTempFile { path, file },
error,
}),
}
}
/// Securely reopen the temporary file.
///
/// This function is useful when you need multiple independent handles to
/// the same file. It's perfectly fine to drop the original `NamedTempFile`
/// while holding on to `File`s returned by this function; the `File`s will
/// remain usable. However, they may not be nameable.
///
/// # Errors
///
/// If the file cannot be reopened, `Err` is returned.
///
/// # Security
///
/// Unlike `File::open(my_temp_file.path())`, `NamedTempFile::reopen()`
/// guarantees that the re-opened file is the _same_ file, even in the
/// presence of pathological temporary file cleaners.
///
/// # Examples
///
/// ```no_run
/// # use std::io;
/// use tempfile::NamedTempFile;
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let file = NamedTempFile::new()?;
///
/// let another_handle = file.reopen()?;
/// # Ok(())
/// # }
/// ```
pub fn reopen(&self) -> io::Result<File> {
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 {
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 {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.as_file().read(buf).with_err_path(|| self.path())
}
}
impl Write for NamedTempFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.as_file_mut().write(buf).with_err_path(|| self.path())
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.as_file_mut().flush().with_err_path(|| self.path())
}
}
impl<'a> Write for &'a NamedTempFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.as_file().write(buf).with_err_path(|| self.path())
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.as_file().flush().with_err_path(|| self.path())
}
}
impl Seek for NamedTempFile {
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 {
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 {
#[inline]
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
self.as_file().as_raw_fd()
}
}
#[cfg(windows)]
impl std::os::windows::io::AsRawHandle for NamedTempFile {
#[inline]
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
self.as_file().as_raw_handle()
}
}
pub(crate) fn create_named(
mut path: PathBuf,
open_options: &mut OpenOptions,
) -> io::Result<NamedTempFile> {
// Make the path absolute. Otherwise, changing directories could cause us to
// delete the wrong file.
if !path.is_absolute() {
path = env::current_dir()?.join(path)
}
imp::create_named(&path, open_options)
.with_err_path(|| path.clone())
.map(|file| NamedTempFile {
path: TempPath {
path: path.into_boxed_path(),
},
file,
})
}

537
zeroidc/vendor/tempfile/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,537 @@
//! Temporary files and directories.
//!
//! - Use the [`tempfile()`] function for temporary files
//! - Use the [`tempdir()`] function for temporary directories.
//!
//! # Design
//!
//! This crate provides several approaches to creating temporary files and directories.
//! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed.
//! [`TempDir`] and [`NamedTempFile`] both rely on Rust destructors for cleanup.
//!
//! When choosing between the temporary file variants, prefer `tempfile`
//! unless you either need to know the file's path or to be able to persist it.
//!
//! ## 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.
//!
//! ## Security
//!
//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
//!
//! `tempfile` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
//! 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:
//!
//! ```
//! use tempfile::tempfile;
//! use std::io::{self, Write};
//!
//! # fn main() {
//! # if let Err(_) = run() {
//! # ::std::process::exit(1);
//! # }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! // Create a file inside of `std::env::temp_dir()`.
//! let mut file = tempfile()?;
//!
//! writeln!(file, "Brian was here. Briefly.")?;
//! # Ok(())
//! # }
//! ```
//!
//! Create a named temporary file and open an independent file handle:
//!
//! ```
//! use tempfile::NamedTempFile;
//! use std::io::{self, Write, Read};
//!
//! # fn main() {
//! # if let Err(_) = run() {
//! # ::std::process::exit(1);
//! # }
//! # }
//! # fn run() -> Result<(), io::Error> {
//! let text = "Brian was here. Briefly.";
//!
//! // Create a file inside of `std::env::temp_dir()`.
//! let mut file1 = NamedTempFile::new()?;
//!
//! // Re-open it.
//! let mut file2 = file1.reopen()?;
//!
//! // Write some test data to the first handle.
//! file1.write_all(text.as_bytes())?;
//!
//! // Read the test data using the second handle.
//! let mut buf = String::new();
//! file2.read_to_string(&mut buf)?;
//! assert_eq!(buf, text);
//! # Ok(())
//! # }
//! ```
//!
//! Create a temporary directory and add a file to it:
//!
//! ```
//! use tempfile::tempdir;
//! use std::fs::File;
//! use std::io::{self, Write};
//!
//! # 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 dir = tempdir()?;
//!
//! let file_path = dir.path().join("my-temporary-note.txt");
//! let mut file = File::create(file_path)?;
//! writeln!(file, "Brian was here. Briefly.")?;
//!
//! // By closing the `TempDir` explicitly, we can check that it has
//! // been deleted successfully. If we don't close it explicitly,
//! // the directory will still be deleted when `dir` goes out
//! // of scope, but we won't know whether deleting the directory
//! // succeeded.
//! drop(file);
//! dir.close()?;
//! # Ok(())
//! # }
//! ```
//!
//! [`tempfile()`]: fn.tempfile.html
//! [`tempdir()`]: fn.tempdir.html
//! [`TempDir`]: struct.TempDir.html
//! [`NamedTempFile`]: struct.NamedTempFile.html
//! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://docs.rs/tempfile/3.1.0"
)]
#![cfg_attr(test, deny(warnings))]
#![deny(rust_2018_idioms)]
#![allow(clippy::redundant_field_names)]
#![cfg_attr(feature = "nightly", feature(wasi_ext))]
#[cfg(doctest)]
doc_comment::doctest!("../README.md");
const NUM_RETRIES: u32 = 1 << 31;
const NUM_RAND_CHARS: usize = 6;
use std::ffi::OsStr;
use std::fs::OpenOptions;
use std::path::Path;
use std::{env, io};
mod dir;
mod error;
mod file;
mod spooled;
mod util;
pub use crate::dir::{tempdir, tempdir_in, TempDir};
pub use crate::file::{
tempfile, tempfile_in, NamedTempFile, PathPersistError, PersistError, TempPath,
};
pub use crate::spooled::{spooled_tempfile, SpooledTempFile};
/// Create a new temporary file or directory with custom parameters.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Builder<'a, 'b> {
random_len: usize,
prefix: &'a OsStr,
suffix: &'b OsStr,
append: bool,
}
impl<'a, 'b> Default for Builder<'a, 'b> {
fn default() -> Self {
Builder {
random_len: crate::NUM_RAND_CHARS,
prefix: OsStr::new(".tmp"),
suffix: OsStr::new(""),
append: false,
}
}
}
impl<'a, 'b> Builder<'a, 'b> {
/// Create a new `Builder`.
///
/// # Examples
///
/// Create a named temporary file and write some data into it:
///
/// ```
/// # use std::io;
/// # use std::ffi::OsStr;
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// use tempfile::Builder;
///
/// let named_tempfile = Builder::new()
/// .prefix("my-temporary-note")
/// .suffix(".txt")
/// .rand_bytes(5)
/// .tempfile()?;
///
/// let name = named_tempfile
/// .path()
/// .file_name().and_then(OsStr::to_str);
///
/// if let Some(name) = name {
/// assert!(name.starts_with("my-temporary-note"));
/// assert!(name.ends_with(".txt"));
/// assert_eq!(name.len(), "my-temporary-note.txt".len() + 5);
/// }
/// # Ok(())
/// # }
/// ```
///
/// Create a temporary directory and add a file to it:
///
/// ```
/// # use std::io::{self, Write};
/// # use std::fs::File;
/// # use std::ffi::OsStr;
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// use tempfile::Builder;
///
/// let dir = Builder::new()
/// .prefix("my-temporary-dir")
/// .rand_bytes(5)
/// .tempdir()?;
///
/// let file_path = dir.path().join("my-temporary-note.txt");
/// let mut file = File::create(file_path)?;
/// writeln!(file, "Brian was here. Briefly.")?;
///
/// // By closing the `TempDir` explicitly, we can check that it has
/// // been deleted successfully. If we don't close it explicitly,
/// // the directory will still be deleted when `dir` goes out
/// // of scope, but we won't know whether deleting the directory
/// // succeeded.
/// drop(file);
/// dir.close()?;
/// # Ok(())
/// # }
/// ```
pub fn new() -> Self {
Self::default()
}
/// Set a custom filename prefix.
///
/// Path separators are legal but not advisable.
/// Default: `.tmp`.
///
/// # Examples
///
/// ```
/// # use std::io;
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// # use tempfile::Builder;
/// let named_tempfile = Builder::new()
/// .prefix("my-temporary-note")
/// .tempfile()?;
/// # Ok(())
/// # }
/// ```
pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self {
self.prefix = prefix.as_ref();
self
}
/// Set a custom filename suffix.
///
/// Path separators are legal but not advisable.
/// Default: empty.
///
/// # Examples
///
/// ```
/// # use std::io;
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// # use tempfile::Builder;
/// let named_tempfile = Builder::new()
/// .suffix(".txt")
/// .tempfile()?;
/// # Ok(())
/// # }
/// ```
pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self {
self.suffix = suffix.as_ref();
self
}
/// Set the number of random bytes.
///
/// Default: `6`.
///
/// # Examples
///
/// ```
/// # use std::io;
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// # use tempfile::Builder;
/// let named_tempfile = Builder::new()
/// .rand_bytes(5)
/// .tempfile()?;
/// # Ok(())
/// # }
/// ```
pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
self.random_len = rand;
self
}
/// Set the file to be opened in append mode.
///
/// Default: `false`.
///
/// # Examples
///
/// ```
/// # use std::io;
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// # use tempfile::Builder;
/// let named_tempfile = Builder::new()
/// .append(true)
/// .tempfile()?;
/// # Ok(())
/// # }
/// ```
pub fn append(&mut self, append: bool) -> &mut Self {
self.append = append;
self
}
/// Create the named temporary file.
///
/// # Security
///
/// See [the security][security] docs on `NamedTempFile`.
///
/// # Resource leaking
///
/// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
///
/// # Errors
///
/// If the file cannot be created, `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;
/// let tempfile = Builder::new().tempfile()?;
/// # Ok(())
/// # }
/// ```
///
/// [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())
}
/// Create the named temporary file in the specified directory.
///
/// # Security
///
/// See [the security][security] docs on `NamedTempFile`.
///
/// # Resource leaking
///
/// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
///
/// # Errors
///
/// If the file cannot be created, `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;
/// let tempfile = Builder::new().tempfile_in("./")?;
/// # Ok(())
/// # }
/// ```
///
/// [security]: struct.NamedTempFile.html#security
/// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
util::create_helper(
dir.as_ref(),
self.prefix,
self.suffix,
self.random_len,
|path| file::create_named(path, OpenOptions::new().append(self.append)),
)
}
/// Attempts to make a temporary directory inside of `env::temp_dir()` whose
/// name will have the prefix, `prefix`. The directory and
/// everything inside it will be automatically deleted once the
/// returned `TempDir` is destroyed.
///
/// # Resource leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use std::fs::File;
/// use std::io::Write;
/// use tempfile::Builder;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// let tmp_dir = Builder::new().tempdir()?;
/// # Ok(())
/// # }
/// ```
///
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir(&self) -> io::Result<TempDir> {
self.tempdir_in(&env::temp_dir())
}
/// Attempts to make a temporary directory inside of `dir`.
/// The directory and everything inside it will be automatically
/// deleted once the returned `TempDir` is destroyed.
///
/// # Resource leaking
///
/// See [the resource leaking][resource-leaking] docs on `TempDir`.
///
/// # Errors
///
/// If the directory can not be created, `Err` is returned.
///
/// # Examples
///
/// ```
/// use std::fs::{self, File};
/// use std::io::Write;
/// use tempfile::Builder;
///
/// # use std::io;
/// # fn run() -> Result<(), io::Error> {
/// let tmp_dir = Builder::new().tempdir_in("./")?;
/// # Ok(())
/// # }
/// ```
///
/// [resource-leaking]: struct.TempDir.html#resource-leaking
pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
let storage;
let mut dir = dir.as_ref();
if !dir.is_absolute() {
let cur_dir = env::current_dir()?;
storage = cur_dir.join(dir);
dir = &storage;
}
util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create)
}
}

158
zeroidc/vendor/tempfile/src/spooled.rs vendored Normal file
View File

@@ -0,0 +1,158 @@
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)]
pub enum SpooledData {
InMemory(Cursor<Vec<u8>>),
OnDisk(File),
}
/// An object that behaves like a regular temporary file, but keeps data in
/// memory until it reaches a configured size, at which point the data is
/// written to a temporary file on disk, and further operations use the file
/// on disk.
#[derive(Debug)]
pub struct SpooledTempFile {
max_size: usize,
inner: SpooledData,
}
/// Create a new spooled temporary file.
///
/// # Security
///
/// This variant is secure/reliable in the presence of a pathological temporary
/// file cleaner.
///
/// # Resource Leaking
///
/// The temporary file will be automatically removed by the OS when the last
/// handle to it is closed. This doesn't rely on Rust destructors being run, so
/// will (almost) never fail to clean up the temporary file.
///
/// # Examples
///
/// ```
/// use tempfile::spooled_tempfile;
/// use std::io::{self, Write};
///
/// # fn main() {
/// # if let Err(_) = run() {
/// # ::std::process::exit(1);
/// # }
/// # }
/// # fn run() -> Result<(), io::Error> {
/// let mut file = spooled_tempfile(15);
///
/// writeln!(file, "short line")?;
/// assert!(!file.is_rolled());
///
/// // as a result of this write call, the size of the data will exceed
/// // `max_size` (15), so it will be written to a temporary file on disk,
/// // and the in-memory buffer will be dropped
/// writeln!(file, "marvin gardens")?;
/// assert!(file.is_rolled());
///
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn spooled_tempfile(max_size: usize) -> SpooledTempFile {
SpooledTempFile::new(max_size)
}
impl SpooledTempFile {
pub fn new(max_size: usize) -> SpooledTempFile {
SpooledTempFile {
max_size: max_size,
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 {
SpooledData::InMemory(_) => false,
SpooledData::OnDisk(_) => true,
}
}
/// Rolls over to a file on disk, regardless of current size. Does nothing
/// if already rolled over.
pub fn roll(&mut self) -> io::Result<()> {
if !self.is_rolled() {
let mut file = tempfile()?;
if let SpooledData::InMemory(ref mut cursor) = self.inner {
file.write_all(cursor.get_ref())?;
file.seek(SeekFrom::Start(cursor.position()))?;
}
self.inner = SpooledData::OnDisk(file);
}
Ok(())
}
pub fn set_len(&mut self, size: u64) -> Result<(), io::Error> {
if size as usize > self.max_size {
self.roll()?; // does nothing if already rolled over
}
match self.inner {
SpooledData::InMemory(ref mut cursor) => {
cursor.get_mut().resize(size as usize, 0);
Ok(())
}
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 {
SpooledData::InMemory(ref mut cursor) => cursor.read(buf),
SpooledData::OnDisk(ref mut file) => file.read(buf),
}
}
}
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 SpooledData::InMemory(ref mut cursor) = self.inner {
rolling = cursor.position() as usize + buf.len() > self.max_size;
}
if rolling {
self.roll()?;
}
// write the bytes
match self.inner {
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 {
SpooledData::InMemory(ref mut cursor) => cursor.flush(),
SpooledData::OnDisk(ref mut file) => file.flush(),
}
}
}
impl Seek for SpooledTempFile {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match self.inner {
SpooledData::InMemory(ref mut cursor) => cursor.seek(pos),
SpooledData::OnDisk(ref mut file) => file.seek(pos),
}
}
}

48
zeroidc/vendor/tempfile/src/util.rs vendored Normal file
View File

@@ -0,0 +1,48 @@
use fastrand;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
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);
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
}
pub fn create_helper<F, R>(
base: &Path,
prefix: &OsStr,
suffix: &OsStr,
random_len: usize,
f: F,
) -> io::Result<R>
where
F: Fn(PathBuf) -> io::Result<R>,
{
let num_retries = if random_len != 0 {
crate::NUM_RETRIES
} else {
1
};
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,
res => res,
};
}
Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"too many temporary files exist",
))
.with_err_path(|| base)
}