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 //! ## Early drop pitfall 31 //! 32 //! Because `TempDir` and `NamedTempFile` rely on their destructors for cleanup, this can lead 33 //! to an unexpected early removal of the directory/file, usually when working with APIs which are 34 //! generic over `AsRef<Path>`. Consider the following example: 35 //! 36 //! ```no_run 37 //! # use tempfile::tempdir; 38 //! # use std::io; 39 //! # use std::process::Command; 40 //! # fn main() { 41 //! # if let Err(_) = run() { 42 //! # ::std::process::exit(1); 43 //! # } 44 //! # } 45 //! # fn run() -> Result<(), io::Error> { 46 //! // Create a directory inside of `std::env::temp_dir()`. 47 //! let temp_dir = tempdir()?; 48 //! 49 //! // Spawn the `touch` command inside the temporary directory and collect the exit status 50 //! // Note that `temp_dir` is **not** moved into `current_dir`, but passed as a reference 51 //! let exit_status = Command::new("touch").arg("tmp").current_dir(&temp_dir).status()?; 52 //! assert!(exit_status.success()); 53 //! 54 //! # Ok(()) 55 //! # } 56 //! ``` 57 //! 58 //! This works because a reference to `temp_dir` is passed to `current_dir`, resulting in the 59 //! destructor of `temp_dir` being run after the `Command` has finished execution. Moving the 60 //! `TempDir` into the `current_dir` call would result in the `TempDir` being converted into 61 //! an internal representation, with the original value being dropped and the directory thus 62 //! being deleted, before the command can be executed. 63 //! 64 //! The `touch` command would fail with an `No such file or directory` error. 65 //! 66 //! ## Examples 67 //! 68 //! Create a temporary file and write some data into it: 69 //! 70 //! ``` 71 //! use tempfile::tempfile; 72 //! use std::io::{self, Write}; 73 //! 74 //! # fn main() { 75 //! # if let Err(_) = run() { 76 //! # ::std::process::exit(1); 77 //! # } 78 //! # } 79 //! # fn run() -> Result<(), io::Error> { 80 //! // Create a file inside of `std::env::temp_dir()`. 81 //! let mut file = tempfile()?; 82 //! 83 //! writeln!(file, "Brian was here. Briefly.")?; 84 //! # Ok(()) 85 //! # } 86 //! ``` 87 //! 88 //! Create a named temporary file and open an independent file handle: 89 //! 90 //! ``` 91 //! use tempfile::NamedTempFile; 92 //! use std::io::{self, Write, Read}; 93 //! 94 //! # fn main() { 95 //! # if let Err(_) = run() { 96 //! # ::std::process::exit(1); 97 //! # } 98 //! # } 99 //! # fn run() -> Result<(), io::Error> { 100 //! let text = "Brian was here. Briefly."; 101 //! 102 //! // Create a file inside of `std::env::temp_dir()`. 103 //! let mut file1 = NamedTempFile::new()?; 104 //! 105 //! // Re-open it. 106 //! let mut file2 = file1.reopen()?; 107 //! 108 //! // Write some test data to the first handle. 109 //! file1.write_all(text.as_bytes())?; 110 //! 111 //! // Read the test data using the second handle. 112 //! let mut buf = String::new(); 113 //! file2.read_to_string(&mut buf)?; 114 //! assert_eq!(buf, text); 115 //! # Ok(()) 116 //! # } 117 //! ``` 118 //! 119 //! Create a temporary directory and add a file to it: 120 //! 121 //! ``` 122 //! use tempfile::tempdir; 123 //! use std::fs::File; 124 //! use std::io::{self, Write}; 125 //! 126 //! # fn main() { 127 //! # if let Err(_) = run() { 128 //! # ::std::process::exit(1); 129 //! # } 130 //! # } 131 //! # fn run() -> Result<(), io::Error> { 132 //! // Create a directory inside of `std::env::temp_dir()`. 133 //! let dir = tempdir()?; 134 //! 135 //! let file_path = dir.path().join("my-temporary-note.txt"); 136 //! let mut file = File::create(file_path)?; 137 //! writeln!(file, "Brian was here. Briefly.")?; 138 //! 139 //! // By closing the `TempDir` explicitly, we can check that it has 140 //! // been deleted successfully. If we don't close it explicitly, 141 //! // the directory will still be deleted when `dir` goes out 142 //! // of scope, but we won't know whether deleting the directory 143 //! // succeeded. 144 //! drop(file); 145 //! dir.close()?; 146 //! # Ok(()) 147 //! # } 148 //! ``` 149 //! 150 //! [`tempfile()`]: fn.tempfile.html 151 //! [`tempdir()`]: fn.tempdir.html 152 //! [`TempDir`]: struct.TempDir.html 153 //! [`NamedTempFile`]: struct.NamedTempFile.html 154 //! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html 155 156 #![doc( 157 html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", 158 html_favicon_url = "https://www.rust-lang.org/favicon.ico", 159 html_root_url = "https://docs.rs/tempfile/3.1.0" 160 )] 161 #![cfg_attr(test, deny(warnings))] 162 #![deny(rust_2018_idioms)] 163 #![allow(clippy::redundant_field_names)] 164 #![cfg_attr(feature = "nightly", feature(wasi_ext))] 165 166 #[cfg(doctest)] 167 doc_comment::doctest!("../README.md"); 168 169 const NUM_RETRIES: u32 = 1 << 31; 170 const NUM_RAND_CHARS: usize = 6; 171 172 use std::ffi::OsStr; 173 use std::fs::OpenOptions; 174 use std::path::Path; 175 use std::{env, io}; 176 177 mod dir; 178 mod error; 179 mod file; 180 mod spooled; 181 mod util; 182 183 pub use crate::dir::{tempdir, tempdir_in, TempDir}; 184 pub use crate::file::{ 185 tempfile, tempfile_in, NamedTempFile, PathPersistError, PersistError, TempPath, 186 }; 187 pub use crate::spooled::{spooled_tempfile, SpooledTempFile}; 188 189 /// Create a new temporary file or directory with custom parameters. 190 #[derive(Debug, Clone, Eq, PartialEq)] 191 pub struct Builder<'a, 'b> { 192 random_len: usize, 193 prefix: &'a OsStr, 194 suffix: &'b OsStr, 195 append: bool, 196 } 197 198 impl<'a, 'b> Default for Builder<'a, 'b> { default() -> Self199 fn default() -> Self { 200 Builder { 201 random_len: crate::NUM_RAND_CHARS, 202 prefix: OsStr::new(".tmp"), 203 suffix: OsStr::new(""), 204 append: false, 205 } 206 } 207 } 208 209 impl<'a, 'b> Builder<'a, 'b> { 210 /// Create a new `Builder`. 211 /// 212 /// # Examples 213 /// 214 /// Create a named temporary file and write some data into it: 215 /// 216 /// ``` 217 /// # use std::io; 218 /// # use std::ffi::OsStr; 219 /// # fn main() { 220 /// # if let Err(_) = run() { 221 /// # ::std::process::exit(1); 222 /// # } 223 /// # } 224 /// # fn run() -> Result<(), io::Error> { 225 /// use tempfile::Builder; 226 /// 227 /// let named_tempfile = Builder::new() 228 /// .prefix("my-temporary-note") 229 /// .suffix(".txt") 230 /// .rand_bytes(5) 231 /// .tempfile()?; 232 /// 233 /// let name = named_tempfile 234 /// .path() 235 /// .file_name().and_then(OsStr::to_str); 236 /// 237 /// if let Some(name) = name { 238 /// assert!(name.starts_with("my-temporary-note")); 239 /// assert!(name.ends_with(".txt")); 240 /// assert_eq!(name.len(), "my-temporary-note.txt".len() + 5); 241 /// } 242 /// # Ok(()) 243 /// # } 244 /// ``` 245 /// 246 /// Create a temporary directory and add a file to it: 247 /// 248 /// ``` 249 /// # use std::io::{self, Write}; 250 /// # use std::fs::File; 251 /// # use std::ffi::OsStr; 252 /// # fn main() { 253 /// # if let Err(_) = run() { 254 /// # ::std::process::exit(1); 255 /// # } 256 /// # } 257 /// # fn run() -> Result<(), io::Error> { 258 /// use tempfile::Builder; 259 /// 260 /// let dir = Builder::new() 261 /// .prefix("my-temporary-dir") 262 /// .rand_bytes(5) 263 /// .tempdir()?; 264 /// 265 /// let file_path = dir.path().join("my-temporary-note.txt"); 266 /// let mut file = File::create(file_path)?; 267 /// writeln!(file, "Brian was here. Briefly.")?; 268 /// 269 /// // By closing the `TempDir` explicitly, we can check that it has 270 /// // been deleted successfully. If we don't close it explicitly, 271 /// // the directory will still be deleted when `dir` goes out 272 /// // of scope, but we won't know whether deleting the directory 273 /// // succeeded. 274 /// drop(file); 275 /// dir.close()?; 276 /// # Ok(()) 277 /// # } 278 /// ``` new() -> Self279 pub fn new() -> Self { 280 Self::default() 281 } 282 283 /// Set a custom filename prefix. 284 /// 285 /// Path separators are legal but not advisable. 286 /// Default: `.tmp`. 287 /// 288 /// # Examples 289 /// 290 /// ``` 291 /// # use std::io; 292 /// # fn main() { 293 /// # if let Err(_) = run() { 294 /// # ::std::process::exit(1); 295 /// # } 296 /// # } 297 /// # fn run() -> Result<(), io::Error> { 298 /// # use tempfile::Builder; 299 /// let named_tempfile = Builder::new() 300 /// .prefix("my-temporary-note") 301 /// .tempfile()?; 302 /// # Ok(()) 303 /// # } 304 /// ``` prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self305 pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self { 306 self.prefix = prefix.as_ref(); 307 self 308 } 309 310 /// Set a custom filename suffix. 311 /// 312 /// Path separators are legal but not advisable. 313 /// Default: empty. 314 /// 315 /// # Examples 316 /// 317 /// ``` 318 /// # use std::io; 319 /// # fn main() { 320 /// # if let Err(_) = run() { 321 /// # ::std::process::exit(1); 322 /// # } 323 /// # } 324 /// # fn run() -> Result<(), io::Error> { 325 /// # use tempfile::Builder; 326 /// let named_tempfile = Builder::new() 327 /// .suffix(".txt") 328 /// .tempfile()?; 329 /// # Ok(()) 330 /// # } 331 /// ``` suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self332 pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self { 333 self.suffix = suffix.as_ref(); 334 self 335 } 336 337 /// Set the number of random bytes. 338 /// 339 /// Default: `6`. 340 /// 341 /// # Examples 342 /// 343 /// ``` 344 /// # use std::io; 345 /// # fn main() { 346 /// # if let Err(_) = run() { 347 /// # ::std::process::exit(1); 348 /// # } 349 /// # } 350 /// # fn run() -> Result<(), io::Error> { 351 /// # use tempfile::Builder; 352 /// let named_tempfile = Builder::new() 353 /// .rand_bytes(5) 354 /// .tempfile()?; 355 /// # Ok(()) 356 /// # } 357 /// ``` rand_bytes(&mut self, rand: usize) -> &mut Self358 pub fn rand_bytes(&mut self, rand: usize) -> &mut Self { 359 self.random_len = rand; 360 self 361 } 362 363 /// Set the file to be opened in append mode. 364 /// 365 /// Default: `false`. 366 /// 367 /// # Examples 368 /// 369 /// ``` 370 /// # use std::io; 371 /// # fn main() { 372 /// # if let Err(_) = run() { 373 /// # ::std::process::exit(1); 374 /// # } 375 /// # } 376 /// # fn run() -> Result<(), io::Error> { 377 /// # use tempfile::Builder; 378 /// let named_tempfile = Builder::new() 379 /// .append(true) 380 /// .tempfile()?; 381 /// # Ok(()) 382 /// # } 383 /// ``` append(&mut self, append: bool) -> &mut Self384 pub fn append(&mut self, append: bool) -> &mut Self { 385 self.append = append; 386 self 387 } 388 389 /// Create the named temporary file. 390 /// 391 /// # Security 392 /// 393 /// See [the security][security] docs on `NamedTempFile`. 394 /// 395 /// # Resource leaking 396 /// 397 /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`. 398 /// 399 /// # Errors 400 /// 401 /// If the file cannot be created, `Err` is returned. 402 /// 403 /// # Examples 404 /// 405 /// ``` 406 /// # use std::io; 407 /// # fn main() { 408 /// # if let Err(_) = run() { 409 /// # ::std::process::exit(1); 410 /// # } 411 /// # } 412 /// # fn run() -> Result<(), io::Error> { 413 /// # use tempfile::Builder; 414 /// let tempfile = Builder::new().tempfile()?; 415 /// # Ok(()) 416 /// # } 417 /// ``` 418 /// 419 /// [security]: struct.NamedTempFile.html#security 420 /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking tempfile(&self) -> io::Result<NamedTempFile>421 pub fn tempfile(&self) -> io::Result<NamedTempFile> { 422 self.tempfile_in(&env::temp_dir()) 423 } 424 425 /// Create the named temporary file in the specified directory. 426 /// 427 /// # Security 428 /// 429 /// See [the security][security] docs on `NamedTempFile`. 430 /// 431 /// # Resource leaking 432 /// 433 /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`. 434 /// 435 /// # Errors 436 /// 437 /// If the file cannot be created, `Err` is returned. 438 /// 439 /// # Examples 440 /// 441 /// ``` 442 /// # use std::io; 443 /// # fn main() { 444 /// # if let Err(_) = run() { 445 /// # ::std::process::exit(1); 446 /// # } 447 /// # } 448 /// # fn run() -> Result<(), io::Error> { 449 /// # use tempfile::Builder; 450 /// let tempfile = Builder::new().tempfile_in("./")?; 451 /// # Ok(()) 452 /// # } 453 /// ``` 454 /// 455 /// [security]: struct.NamedTempFile.html#security 456 /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile>457 pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> { 458 util::create_helper( 459 dir.as_ref(), 460 self.prefix, 461 self.suffix, 462 self.random_len, 463 |path| file::create_named(path, OpenOptions::new().append(self.append)), 464 ) 465 } 466 467 /// Attempts to make a temporary directory inside of `env::temp_dir()` whose 468 /// name will have the prefix, `prefix`. The directory and 469 /// everything inside it will be automatically deleted once the 470 /// returned `TempDir` is destroyed. 471 /// 472 /// # Resource leaking 473 /// 474 /// See [the resource leaking][resource-leaking] docs on `TempDir`. 475 /// 476 /// # Errors 477 /// 478 /// If the directory can not be created, `Err` is returned. 479 /// 480 /// # Examples 481 /// 482 /// ``` 483 /// use std::fs::File; 484 /// use std::io::Write; 485 /// use tempfile::Builder; 486 /// 487 /// # use std::io; 488 /// # fn run() -> Result<(), io::Error> { 489 /// let tmp_dir = Builder::new().tempdir()?; 490 /// # Ok(()) 491 /// # } 492 /// ``` 493 /// 494 /// [resource-leaking]: struct.TempDir.html#resource-leaking tempdir(&self) -> io::Result<TempDir>495 pub fn tempdir(&self) -> io::Result<TempDir> { 496 self.tempdir_in(&env::temp_dir()) 497 } 498 499 /// Attempts to make a temporary directory inside of `dir`. 500 /// The directory and everything inside it will be automatically 501 /// deleted once the returned `TempDir` is destroyed. 502 /// 503 /// # Resource leaking 504 /// 505 /// See [the resource leaking][resource-leaking] docs on `TempDir`. 506 /// 507 /// # Errors 508 /// 509 /// If the directory can not be created, `Err` is returned. 510 /// 511 /// # Examples 512 /// 513 /// ``` 514 /// use std::fs::{self, File}; 515 /// use std::io::Write; 516 /// use tempfile::Builder; 517 /// 518 /// # use std::io; 519 /// # fn run() -> Result<(), io::Error> { 520 /// let tmp_dir = Builder::new().tempdir_in("./")?; 521 /// # Ok(()) 522 /// # } 523 /// ``` 524 /// 525 /// [resource-leaking]: struct.TempDir.html#resource-leaking tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir>526 pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> { 527 let storage; 528 let mut dir = dir.as_ref(); 529 if !dir.is_absolute() { 530 let cur_dir = env::current_dir()?; 531 storage = cur_dir.join(dir); 532 dir = &storage; 533 } 534 535 util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create) 536 } 537 } 538