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}