1 //! Temporary files and directories. 2 //! 3 //! - Use the [`tempfile()`] function for temporary files 4 //! - Use the [`tempdir()`] function for temporary directories. 5 //! 6 //! # Design 7 //! 8 //! This crate provides several approaches to creating temporary files and directories. 9 //! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed. 10 //! [`TempDir`] and [`NamedTempFile`] both rely on Rust destructors for cleanup. 11 //! 12 //! When choosing between the temporary file variants, prefer `tempfile` 13 //! unless you either need to know the file's path or to be able to persist it. 14 //! 15 //! ## Resource Leaking 16 //! 17 //! `tempfile` will (almost) never fail to cleanup temporary resources, but `TempDir` and `NamedTempFile` will if 18 //! their destructors don't run. This is because `tempfile` relies on the OS to cleanup the 19 //! underlying file, while `TempDir` and `NamedTempFile` rely on their destructors to do so. 20 //! 21 //! ## Security 22 //! 23 //! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because 24 //! a temporary file cleaner could delete the temporary file which an attacker could then replace. 25 //! 26 //! `tempfile` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does 27 //! rely on file paths for _some_ operations. See the security documentation on 28 //! the `NamedTempFile` type for more information. 29 //! 30 //! ## Examples 31 //! 32 //! Create a temporary file and write some data into it: 33 //! 34 //! ``` 35 //! use tempfile::tempfile; 36 //! use std::io::{self, Write}; 37 //! 38 //! # fn main() { 39 //! # if let Err(_) = run() { 40 //! # ::std::process::exit(1); 41 //! # } 42 //! # } 43 //! # fn run() -> Result<(), io::Error> { 44 //! // Create a file inside of `std::env::temp_dir()`. 45 //! let mut file = tempfile()?; 46 //! 47 //! writeln!(file, "Brian was here. Briefly.")?; 48 //! # Ok(()) 49 //! # } 50 //! ``` 51 //! 52 //! Create a named temporary file and open an independent file handle: 53 //! 54 //! ``` 55 //! use tempfile::NamedTempFile; 56 //! use std::io::{self, Write, Read}; 57 //! 58 //! # fn main() { 59 //! # if let Err(_) = run() { 60 //! # ::std::process::exit(1); 61 //! # } 62 //! # } 63 //! # fn run() -> Result<(), io::Error> { 64 //! let text = "Brian was here. Briefly."; 65 //! 66 //! // Create a file inside of `std::env::temp_dir()`. 67 //! let mut file1 = NamedTempFile::new()?; 68 //! 69 //! // Re-open it. 70 //! let mut file2 = file1.reopen()?; 71 //! 72 //! // Write some test data to the first handle. 73 //! file1.write_all(text.as_bytes())?; 74 //! 75 //! // Read the test data using the second handle. 76 //! let mut buf = String::new(); 77 //! file2.read_to_string(&mut buf)?; 78 //! assert_eq!(buf, text); 79 //! # Ok(()) 80 //! # } 81 //! ``` 82 //! 83 //! Create a temporary directory and add a file to it: 84 //! 85 //! ``` 86 //! use tempfile::tempdir; 87 //! use std::fs::File; 88 //! use std::io::{self, Write}; 89 //! 90 //! # fn main() { 91 //! # if let Err(_) = run() { 92 //! # ::std::process::exit(1); 93 //! # } 94 //! # } 95 //! # fn run() -> Result<(), io::Error> { 96 //! // Create a directory inside of `std::env::temp_dir()`. 97 //! let dir = tempdir()?; 98 //! 99 //! let file_path = dir.path().join("my-temporary-note.txt"); 100 //! let mut file = File::create(file_path)?; 101 //! writeln!(file, "Brian was here. Briefly.")?; 102 //! 103 //! // By closing the `TempDir` explicitly, we can check that it has 104 //! // been deleted successfully. If we don't close it explicitly, 105 //! // the directory will still be deleted when `dir` goes out 106 //! // of scope, but we won't know whether deleting the directory 107 //! // succeeded. 108 //! drop(file); 109 //! dir.close()?; 110 //! # Ok(()) 111 //! # } 112 //! ``` 113 //! 114 //! [`tempfile()`]: fn.tempfile.html 115 //! [`tempdir()`]: fn.tempdir.html 116 //! [`TempDir`]: struct.TempDir.html 117 //! [`NamedTempFile`]: struct.NamedTempFile.html 118 //! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html 119 120 #![doc( 121 html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", 122 html_favicon_url = "https://www.rust-lang.org/favicon.ico", 123 html_root_url = "https://docs.rs/tempfile/3.1.0" 124 )] 125 #![cfg_attr(test, deny(warnings))] 126 #![deny(rust_2018_idioms)] 127 #![allow(clippy::redundant_field_names)] 128 #![cfg_attr(feature = "nightly", feature(wasi_ext))] 129 130 #[macro_use] 131 extern crate cfg_if; 132 133 #[cfg(doctest)] 134 #[macro_use] 135 extern crate doc_comment; 136 137 #[cfg(doctest)] 138 doctest!("../README.md"); 139 140 const NUM_RETRIES: u32 = 1 << 31; 141 const NUM_RAND_CHARS: usize = 6; 142 143 use std::ffi::OsStr; 144 use std::fs::OpenOptions; 145 use std::path::Path; 146 use std::{env, io}; 147 148 mod dir; 149 mod error; 150 mod file; 151 mod spooled; 152 mod util; 153 154 pub use crate::dir::{tempdir, tempdir_in, TempDir}; 155 pub use crate::file::{ 156 tempfile, tempfile_in, NamedTempFile, PathPersistError, PersistError, TempPath, 157 }; 158 pub use crate::spooled::{spooled_tempfile, SpooledTempFile}; 159 160 /// Create a new temporary file or directory with custom parameters. 161 #[derive(Debug, Clone, Eq, PartialEq)] 162 pub struct Builder<'a, 'b> { 163 random_len: usize, 164 prefix: &'a OsStr, 165 suffix: &'b OsStr, 166 append: bool, 167 } 168 169 impl<'a, 'b> Default for Builder<'a, 'b> { default() -> Self170 fn default() -> Self { 171 Builder { 172 random_len: crate::NUM_RAND_CHARS, 173 prefix: OsStr::new(".tmp"), 174 suffix: OsStr::new(""), 175 append: false, 176 } 177 } 178 } 179 180 impl<'a, 'b> Builder<'a, 'b> { 181 /// Create a new `Builder`. 182 /// 183 /// # Examples 184 /// 185 /// Create a named temporary file and write some data into it: 186 /// 187 /// ``` 188 /// # use std::io; 189 /// # use std::ffi::OsStr; 190 /// # fn main() { 191 /// # if let Err(_) = run() { 192 /// # ::std::process::exit(1); 193 /// # } 194 /// # } 195 /// # fn run() -> Result<(), io::Error> { 196 /// use tempfile::Builder; 197 /// 198 /// let named_tempfile = Builder::new() 199 /// .prefix("my-temporary-note") 200 /// .suffix(".txt") 201 /// .rand_bytes(5) 202 /// .tempfile()?; 203 /// 204 /// let name = named_tempfile 205 /// .path() 206 /// .file_name().and_then(OsStr::to_str); 207 /// 208 /// if let Some(name) = name { 209 /// assert!(name.starts_with("my-temporary-note")); 210 /// assert!(name.ends_with(".txt")); 211 /// assert_eq!(name.len(), "my-temporary-note.txt".len() + 5); 212 /// } 213 /// # Ok(()) 214 /// # } 215 /// ``` 216 /// 217 /// Create a temporary directory and add a file to it: 218 /// 219 /// ``` 220 /// # use std::io::{self, Write}; 221 /// # use std::fs::File; 222 /// # use std::ffi::OsStr; 223 /// # fn main() { 224 /// # if let Err(_) = run() { 225 /// # ::std::process::exit(1); 226 /// # } 227 /// # } 228 /// # fn run() -> Result<(), io::Error> { 229 /// use tempfile::Builder; 230 /// 231 /// let dir = Builder::new() 232 /// .prefix("my-temporary-dir") 233 /// .rand_bytes(5) 234 /// .tempdir()?; 235 /// 236 /// let file_path = dir.path().join("my-temporary-note.txt"); 237 /// let mut file = File::create(file_path)?; 238 /// writeln!(file, "Brian was here. Briefly.")?; 239 /// 240 /// // By closing the `TempDir` explicitly, we can check that it has 241 /// // been deleted successfully. If we don't close it explicitly, 242 /// // the directory will still be deleted when `dir` goes out 243 /// // of scope, but we won't know whether deleting the directory 244 /// // succeeded. 245 /// drop(file); 246 /// dir.close()?; 247 /// # Ok(()) 248 /// # } 249 /// ``` new() -> Self250 pub fn new() -> Self { 251 Self::default() 252 } 253 254 /// Set a custom filename prefix. 255 /// 256 /// Path separators are legal but not advisable. 257 /// Default: `.tmp`. 258 /// 259 /// # Examples 260 /// 261 /// ``` 262 /// # use std::io; 263 /// # fn main() { 264 /// # if let Err(_) = run() { 265 /// # ::std::process::exit(1); 266 /// # } 267 /// # } 268 /// # fn run() -> Result<(), io::Error> { 269 /// # use tempfile::Builder; 270 /// let named_tempfile = Builder::new() 271 /// .prefix("my-temporary-note") 272 /// .tempfile()?; 273 /// # Ok(()) 274 /// # } 275 /// ``` prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self276 pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self { 277 self.prefix = prefix.as_ref(); 278 self 279 } 280 281 /// Set a custom filename suffix. 282 /// 283 /// Path separators are legal but not advisable. 284 /// Default: empty. 285 /// 286 /// # Examples 287 /// 288 /// ``` 289 /// # use std::io; 290 /// # fn main() { 291 /// # if let Err(_) = run() { 292 /// # ::std::process::exit(1); 293 /// # } 294 /// # } 295 /// # fn run() -> Result<(), io::Error> { 296 /// # use tempfile::Builder; 297 /// let named_tempfile = Builder::new() 298 /// .suffix(".txt") 299 /// .tempfile()?; 300 /// # Ok(()) 301 /// # } 302 /// ``` suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self303 pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self { 304 self.suffix = suffix.as_ref(); 305 self 306 } 307 308 /// Set the number of random bytes. 309 /// 310 /// Default: `6`. 311 /// 312 /// # Examples 313 /// 314 /// ``` 315 /// # use std::io; 316 /// # fn main() { 317 /// # if let Err(_) = run() { 318 /// # ::std::process::exit(1); 319 /// # } 320 /// # } 321 /// # fn run() -> Result<(), io::Error> { 322 /// # use tempfile::Builder; 323 /// let named_tempfile = Builder::new() 324 /// .rand_bytes(5) 325 /// .tempfile()?; 326 /// # Ok(()) 327 /// # } 328 /// ``` rand_bytes(&mut self, rand: usize) -> &mut Self329 pub fn rand_bytes(&mut self, rand: usize) -> &mut Self { 330 self.random_len = rand; 331 self 332 } 333 334 /// Set the file to be opened in append mode. 335 /// 336 /// Default: `false`. 337 /// 338 /// # Examples 339 /// 340 /// ``` 341 /// # use std::io; 342 /// # fn main() { 343 /// # if let Err(_) = run() { 344 /// # ::std::process::exit(1); 345 /// # } 346 /// # } 347 /// # fn run() -> Result<(), io::Error> { 348 /// # use tempfile::Builder; 349 /// let named_tempfile = Builder::new() 350 /// .append(true) 351 /// .tempfile()?; 352 /// # Ok(()) 353 /// # } 354 /// ``` append(&mut self, append: bool) -> &mut Self355 pub fn append(&mut self, append: bool) -> &mut Self { 356 self.append = append; 357 self 358 } 359 360 /// Create the named temporary file. 361 /// 362 /// # Security 363 /// 364 /// See [the security][security] docs on `NamedTempFile`. 365 /// 366 /// # Resource leaking 367 /// 368 /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`. 369 /// 370 /// # Errors 371 /// 372 /// If the file cannot be created, `Err` is returned. 373 /// 374 /// # Examples 375 /// 376 /// ``` 377 /// # use std::io; 378 /// # fn main() { 379 /// # if let Err(_) = run() { 380 /// # ::std::process::exit(1); 381 /// # } 382 /// # } 383 /// # fn run() -> Result<(), io::Error> { 384 /// # use tempfile::Builder; 385 /// let tempfile = Builder::new().tempfile()?; 386 /// # Ok(()) 387 /// # } 388 /// ``` 389 /// 390 /// [security]: struct.NamedTempFile.html#security 391 /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking tempfile(&self) -> io::Result<NamedTempFile>392 pub fn tempfile(&self) -> io::Result<NamedTempFile> { 393 self.tempfile_in(&env::temp_dir()) 394 } 395 396 /// Create the named temporary file in the specified directory. 397 /// 398 /// # Security 399 /// 400 /// See [the security][security] docs on `NamedTempFile`. 401 /// 402 /// # Resource leaking 403 /// 404 /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`. 405 /// 406 /// # Errors 407 /// 408 /// If the file cannot be created, `Err` is returned. 409 /// 410 /// # Examples 411 /// 412 /// ``` 413 /// # use std::io; 414 /// # fn main() { 415 /// # if let Err(_) = run() { 416 /// # ::std::process::exit(1); 417 /// # } 418 /// # } 419 /// # fn run() -> Result<(), io::Error> { 420 /// # use tempfile::Builder; 421 /// let tempfile = Builder::new().tempfile_in("./")?; 422 /// # Ok(()) 423 /// # } 424 /// ``` 425 /// 426 /// [security]: struct.NamedTempFile.html#security 427 /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile>428 pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> { 429 util::create_helper( 430 dir.as_ref(), 431 self.prefix, 432 self.suffix, 433 self.random_len, 434 |path| file::create_named(path, OpenOptions::new().append(self.append)), 435 ) 436 } 437 438 /// Attempts to make a temporary directory inside of `env::temp_dir()` whose 439 /// name will have the prefix, `prefix`. The directory and 440 /// everything inside it will be automatically deleted once the 441 /// returned `TempDir` is destroyed. 442 /// 443 /// # Resource leaking 444 /// 445 /// See [the resource leaking][resource-leaking] docs on `TempDir`. 446 /// 447 /// # Errors 448 /// 449 /// If the directory can not be created, `Err` is returned. 450 /// 451 /// # Examples 452 /// 453 /// ``` 454 /// use std::fs::File; 455 /// use std::io::Write; 456 /// use tempfile::Builder; 457 /// 458 /// # use std::io; 459 /// # fn run() -> Result<(), io::Error> { 460 /// let tmp_dir = Builder::new().tempdir()?; 461 /// # Ok(()) 462 /// # } 463 /// ``` 464 /// 465 /// [resource-leaking]: struct.TempDir.html#resource-leaking tempdir(&self) -> io::Result<TempDir>466 pub fn tempdir(&self) -> io::Result<TempDir> { 467 self.tempdir_in(&env::temp_dir()) 468 } 469 470 /// Attempts to make a temporary directory inside of `dir`. 471 /// The directory and everything inside it will be automatically 472 /// deleted once the returned `TempDir` is destroyed. 473 /// 474 /// # Resource leaking 475 /// 476 /// See [the resource leaking][resource-leaking] docs on `TempDir`. 477 /// 478 /// # Errors 479 /// 480 /// If the directory can not be created, `Err` is returned. 481 /// 482 /// # Examples 483 /// 484 /// ``` 485 /// use std::fs::{self, File}; 486 /// use std::io::Write; 487 /// use tempfile::Builder; 488 /// 489 /// # use std::io; 490 /// # fn run() -> Result<(), io::Error> { 491 /// let tmp_dir = Builder::new().tempdir_in("./")?; 492 /// # Ok(()) 493 /// # } 494 /// ``` 495 /// 496 /// [resource-leaking]: struct.TempDir.html#resource-leaking tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir>497 pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> { 498 let storage; 499 let mut dir = dir.as_ref(); 500 if !dir.is_absolute() { 501 let cur_dir = env::current_dir()?; 502 storage = cur_dir.join(dir); 503 dir = &storage; 504 } 505 506 util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create) 507 } 508 } 509