blob: e5b6bd33b0dcfc947d08d06c0d5762e1ba25d9c5 [file] [log] [blame]
//! 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.
//!
//! ## Examples
//!
//! Create a temporary file and write some data into it:
//!
//! ```
//! # extern crate tempfile;
//! 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 temporary directory and add a file to it:
//!
//! ```
//! # extern crate tempfile;
//! 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/2.2.0")]
#![cfg_attr(test, deny(warnings))]
#[macro_use]
extern crate cfg_if;
extern crate rand;
extern crate remove_dir_all;
#[cfg(unix)]
extern crate libc;
#[cfg(windows)]
extern crate winapi;
#[cfg(target_os = "redox")]
extern crate syscall;
const NUM_RETRIES: u32 = 1 << 31;
const NUM_RAND_CHARS: usize = 6;
use std::path::Path;
use std::{env, io};
mod error;
mod dir;
mod file;
mod util;
mod spooled;
pub use dir::{tempdir, tempdir_in, TempDir};
pub use file::{tempfile, tempfile_in, NamedTempFile, PersistError, TempPath};
pub use 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 str,
suffix: &'b str,
}
impl<'a, 'b> Default for Builder<'a, 'b> {
fn default() -> Self {
Builder {
random_len: ::NUM_RAND_CHARS,
prefix: ".tmp",
suffix: "",
}
}
}
impl<'a, 'b> Builder<'a, 'b> {
/// Create a new `Builder`.
///
/// # Examples
///
/// Create a named temporary file and write some data into it:
///
/// ```
/// # extern crate tempfile;
/// # 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:
///
/// ```
/// # extern crate tempfile;
/// # 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
///
/// ```
/// # extern crate tempfile;
/// # 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(&mut self, prefix: &'a str) -> &mut Self {
self.prefix = prefix;
self
}
/// Set a custom filename suffix.
///
/// Path separators are legal but not advisable.
/// Default: empty.
///
/// # Examples
///
/// ```
/// # extern crate tempfile;
/// # 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(&mut self, suffix: &'b str) -> &mut Self {
self.suffix = suffix;
self
}
/// Set the number of random bytes.
///
/// Default: `6`.
///
/// # Examples
///
/// ```
/// # extern crate tempfile;
/// # 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
}
/// 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
///
/// ```
/// # extern crate tempfile;
/// # 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
///
/// ```
/// # extern crate tempfile;
/// # 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,
file::create_named,
)
}
/// 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)
}
}