tempfile/lib.rs
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
129#[macro_use]
130extern crate cfg_if;
131
132const NUM_RETRIES: u32 = 1 << 31;
133const NUM_RAND_CHARS: usize = 6;
134
135use std::ffi::OsStr;
136use std::fs::OpenOptions;
137use std::path::Path;
138use std::{env, io};
139
140mod dir;
141mod error;
142mod file;
143mod spooled;
144mod util;
145
146pub use crate::dir::{tempdir, tempdir_in, TempDir};
147pub use crate::file::{
148 tempfile, tempfile_in, NamedTempFile, PathPersistError, PersistError, TempPath,
149};
150pub use crate::spooled::{spooled_tempfile, SpooledTempFile};
151
152/// Create a new temporary file or directory with custom parameters.
153#[derive(Debug, Clone, Eq, PartialEq)]
154pub struct Builder<'a, 'b> {
155 random_len: usize,
156 prefix: &'a OsStr,
157 suffix: &'b OsStr,
158 append: bool,
159}
160
161impl<'a, 'b> Default for Builder<'a, 'b> {
162 fn default() -> Self {
163 Builder {
164 random_len: crate::NUM_RAND_CHARS,
165 prefix: OsStr::new(".tmp"),
166 suffix: OsStr::new(""),
167 append: false,
168 }
169 }
170}
171
172impl<'a, 'b> Builder<'a, 'b> {
173 /// Create a new `Builder`.
174 ///
175 /// # Examples
176 ///
177 /// Create a named temporary file and write some data into it:
178 ///
179 /// ```
180 /// # use std::io;
181 /// # use std::ffi::OsStr;
182 /// # fn main() {
183 /// # if let Err(_) = run() {
184 /// # ::std::process::exit(1);
185 /// # }
186 /// # }
187 /// # fn run() -> Result<(), io::Error> {
188 /// use tempfile::Builder;
189 ///
190 /// let named_tempfile = Builder::new()
191 /// .prefix("my-temporary-note")
192 /// .suffix(".txt")
193 /// .rand_bytes(5)
194 /// .tempfile()?;
195 ///
196 /// let name = named_tempfile
197 /// .path()
198 /// .file_name().and_then(OsStr::to_str);
199 ///
200 /// if let Some(name) = name {
201 /// assert!(name.starts_with("my-temporary-note"));
202 /// assert!(name.ends_with(".txt"));
203 /// assert_eq!(name.len(), "my-temporary-note.txt".len() + 5);
204 /// }
205 /// # Ok(())
206 /// # }
207 /// ```
208 ///
209 /// Create a temporary directory and add a file to it:
210 ///
211 /// ```
212 /// # use std::io::{self, Write};
213 /// # use std::fs::File;
214 /// # use std::ffi::OsStr;
215 /// # fn main() {
216 /// # if let Err(_) = run() {
217 /// # ::std::process::exit(1);
218 /// # }
219 /// # }
220 /// # fn run() -> Result<(), io::Error> {
221 /// use tempfile::Builder;
222 ///
223 /// let dir = Builder::new()
224 /// .prefix("my-temporary-dir")
225 /// .rand_bytes(5)
226 /// .tempdir()?;
227 ///
228 /// let file_path = dir.path().join("my-temporary-note.txt");
229 /// let mut file = File::create(file_path)?;
230 /// writeln!(file, "Brian was here. Briefly.")?;
231 ///
232 /// // By closing the `TempDir` explicitly, we can check that it has
233 /// // been deleted successfully. If we don't close it explicitly,
234 /// // the directory will still be deleted when `dir` goes out
235 /// // of scope, but we won't know whether deleting the directory
236 /// // succeeded.
237 /// drop(file);
238 /// dir.close()?;
239 /// # Ok(())
240 /// # }
241 /// ```
242 pub fn new() -> Self {
243 Self::default()
244 }
245
246 /// Set a custom filename prefix.
247 ///
248 /// Path separators are legal but not advisable.
249 /// Default: `.tmp`.
250 ///
251 /// # Examples
252 ///
253 /// ```
254 /// # use std::io;
255 /// # fn main() {
256 /// # if let Err(_) = run() {
257 /// # ::std::process::exit(1);
258 /// # }
259 /// # }
260 /// # fn run() -> Result<(), io::Error> {
261 /// # use tempfile::Builder;
262 /// let named_tempfile = Builder::new()
263 /// .prefix("my-temporary-note")
264 /// .tempfile()?;
265 /// # Ok(())
266 /// # }
267 /// ```
268 pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self {
269 self.prefix = prefix.as_ref();
270 self
271 }
272
273 /// Set a custom filename suffix.
274 ///
275 /// Path separators are legal but not advisable.
276 /// Default: empty.
277 ///
278 /// # Examples
279 ///
280 /// ```
281 /// # use std::io;
282 /// # fn main() {
283 /// # if let Err(_) = run() {
284 /// # ::std::process::exit(1);
285 /// # }
286 /// # }
287 /// # fn run() -> Result<(), io::Error> {
288 /// # use tempfile::Builder;
289 /// let named_tempfile = Builder::new()
290 /// .suffix(".txt")
291 /// .tempfile()?;
292 /// # Ok(())
293 /// # }
294 /// ```
295 pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self {
296 self.suffix = suffix.as_ref();
297 self
298 }
299
300 /// Set the number of random bytes.
301 ///
302 /// Default: `6`.
303 ///
304 /// # Examples
305 ///
306 /// ```
307 /// # use std::io;
308 /// # fn main() {
309 /// # if let Err(_) = run() {
310 /// # ::std::process::exit(1);
311 /// # }
312 /// # }
313 /// # fn run() -> Result<(), io::Error> {
314 /// # use tempfile::Builder;
315 /// let named_tempfile = Builder::new()
316 /// .rand_bytes(5)
317 /// .tempfile()?;
318 /// # Ok(())
319 /// # }
320 /// ```
321 pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
322 self.random_len = rand;
323 self
324 }
325
326 /// Set the file to be opened in append mode.
327 ///
328 /// Default: `false`.
329 ///
330 /// # Examples
331 ///
332 /// ```
333 /// # use std::io;
334 /// # fn main() {
335 /// # if let Err(_) = run() {
336 /// # ::std::process::exit(1);
337 /// # }
338 /// # }
339 /// # fn run() -> Result<(), io::Error> {
340 /// # use tempfile::Builder;
341 /// let named_tempfile = Builder::new()
342 /// .append(true)
343 /// .tempfile()?;
344 /// # Ok(())
345 /// # }
346 /// ```
347 pub fn append(&mut self, append: bool) -> &mut Self {
348 self.append = append;
349 self
350 }
351
352 /// Create the named temporary file.
353 ///
354 /// # Security
355 ///
356 /// See [the security][security] docs on `NamedTempFile`.
357 ///
358 /// # Resource leaking
359 ///
360 /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
361 ///
362 /// # Errors
363 ///
364 /// If the file cannot be created, `Err` is returned.
365 ///
366 /// # Examples
367 ///
368 /// ```
369 /// # use std::io;
370 /// # fn main() {
371 /// # if let Err(_) = run() {
372 /// # ::std::process::exit(1);
373 /// # }
374 /// # }
375 /// # fn run() -> Result<(), io::Error> {
376 /// # use tempfile::Builder;
377 /// let tempfile = Builder::new().tempfile()?;
378 /// # Ok(())
379 /// # }
380 /// ```
381 ///
382 /// [security]: struct.NamedTempFile.html#security
383 /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
384 pub fn tempfile(&self) -> io::Result<NamedTempFile> {
385 self.tempfile_in(&env::temp_dir())
386 }
387
388 /// Create the named temporary file in the specified directory.
389 ///
390 /// # Security
391 ///
392 /// See [the security][security] docs on `NamedTempFile`.
393 ///
394 /// # Resource leaking
395 ///
396 /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
397 ///
398 /// # Errors
399 ///
400 /// If the file cannot be created, `Err` is returned.
401 ///
402 /// # Examples
403 ///
404 /// ```
405 /// # use std::io;
406 /// # fn main() {
407 /// # if let Err(_) = run() {
408 /// # ::std::process::exit(1);
409 /// # }
410 /// # }
411 /// # fn run() -> Result<(), io::Error> {
412 /// # use tempfile::Builder;
413 /// let tempfile = Builder::new().tempfile_in("./")?;
414 /// # Ok(())
415 /// # }
416 /// ```
417 ///
418 /// [security]: struct.NamedTempFile.html#security
419 /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
420 pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
421 util::create_helper(
422 dir.as_ref(),
423 self.prefix,
424 self.suffix,
425 self.random_len,
426 |path| file::create_named(path, OpenOptions::new().append(self.append)),
427 )
428 }
429
430 /// Attempts to make a temporary directory inside of `env::temp_dir()` whose
431 /// name will have the prefix, `prefix`. The directory and
432 /// everything inside it will be automatically deleted once the
433 /// returned `TempDir` is destroyed.
434 ///
435 /// # Resource leaking
436 ///
437 /// See [the resource leaking][resource-leaking] docs on `TempDir`.
438 ///
439 /// # Errors
440 ///
441 /// If the directory can not be created, `Err` is returned.
442 ///
443 /// # Examples
444 ///
445 /// ```
446 /// use std::fs::File;
447 /// use std::io::Write;
448 /// use tempfile::Builder;
449 ///
450 /// # use std::io;
451 /// # fn run() -> Result<(), io::Error> {
452 /// let tmp_dir = Builder::new().tempdir()?;
453 /// # Ok(())
454 /// # }
455 /// ```
456 ///
457 /// [resource-leaking]: struct.TempDir.html#resource-leaking
458 pub fn tempdir(&self) -> io::Result<TempDir> {
459 self.tempdir_in(&env::temp_dir())
460 }
461
462 /// Attempts to make a temporary directory inside of `dir`.
463 /// The directory and everything inside it will be automatically
464 /// deleted once the returned `TempDir` is destroyed.
465 ///
466 /// # Resource leaking
467 ///
468 /// See [the resource leaking][resource-leaking] docs on `TempDir`.
469 ///
470 /// # Errors
471 ///
472 /// If the directory can not be created, `Err` is returned.
473 ///
474 /// # Examples
475 ///
476 /// ```
477 /// use std::fs::{self, File};
478 /// use std::io::Write;
479 /// use tempfile::Builder;
480 ///
481 /// # use std::io;
482 /// # fn run() -> Result<(), io::Error> {
483 /// let tmp_dir = Builder::new().tempdir_in("./")?;
484 /// # Ok(())
485 /// # }
486 /// ```
487 ///
488 /// [resource-leaking]: struct.TempDir.html#resource-leaking
489 pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
490 let storage;
491 let mut dir = dir.as_ref();
492 if !dir.is_absolute() {
493 let cur_dir = env::current_dir()?;
494 storage = cur_dir.join(dir);
495 dir = &storage;
496 }
497
498 util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create)
499 }
500}