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. However `TempDir` and `NamedTempFile` will
18//! fail if 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 rust destructors to do so.
20//! Destructors may fail to run if the process exits through an unhandled signal interrupt (like `SIGINT`),
21//! or if the instance is declared statically (like with [`lazy_static`]), among other possible
22//! reasons.
23//!
24//! ## Security
25//!
26//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
27//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
28//!
29//! `tempfile` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
30//! rely on file paths for _some_ operations. See the security documentation on
31//! the `NamedTempFile` type for more information.
32//!
33//! ## Early drop pitfall
34//!
35//! Because `TempDir` and `NamedTempFile` rely on their destructors for cleanup, this can lead
36//! to an unexpected early removal of the directory/file, usually when working with APIs which are
37//! generic over `AsRef<Path>`. Consider the following example:
38//!
39//! ```no_run
40//! # use tempfile::tempdir;
41//! # use std::io;
42//! # use std::process::Command;
43//! # fn main() {
44//! #     if let Err(_) = run() {
45//! #         ::std::process::exit(1);
46//! #     }
47//! # }
48//! # fn run() -> Result<(), io::Error> {
49//! // Create a directory inside of `std::env::temp_dir()`.
50//! let temp_dir = tempdir()?;
51//!
52//! // Spawn the `touch` command inside the temporary directory and collect the exit status
53//! // Note that `temp_dir` is **not** moved into `current_dir`, but passed as a reference
54//! let exit_status = Command::new("touch").arg("tmp").current_dir(&temp_dir).status()?;
55//! assert!(exit_status.success());
56//!
57//! # Ok(())
58//! # }
59//! ```
60//!
61//! This works because a reference to `temp_dir` is passed to `current_dir`, resulting in the
62//! destructor of `temp_dir` being run after the `Command` has finished execution. Moving the
63//! `TempDir` into the `current_dir` call would result in the `TempDir` being converted into
64//! an internal representation, with the original value being dropped and the directory thus
65//! being deleted, before the command can be executed.
66//!
67//! The `touch` command would fail with an `No such file or directory` error.
68//!
69//! ## Examples
70//!
71//! Create a temporary file and write some data into it:
72//!
73//! ```
74//! use tempfile::tempfile;
75//! use std::io::{self, Write};
76//!
77//! # fn main() {
78//! #     if let Err(_) = run() {
79//! #         ::std::process::exit(1);
80//! #     }
81//! # }
82//! # fn run() -> Result<(), io::Error> {
83//! // Create a file inside of `std::env::temp_dir()`.
84//! let mut file = tempfile()?;
85//!
86//! writeln!(file, "Brian was here. Briefly.")?;
87//! # Ok(())
88//! # }
89//! ```
90//!
91//! Create a named temporary file and open an independent file handle:
92//!
93//! ```
94//! use tempfile::NamedTempFile;
95//! use std::io::{self, Write, Read};
96//!
97//! # fn main() {
98//! #     if let Err(_) = run() {
99//! #         ::std::process::exit(1);
100//! #     }
101//! # }
102//! # fn run() -> Result<(), io::Error> {
103//! let text = "Brian was here. Briefly.";
104//!
105//! // Create a file inside of `std::env::temp_dir()`.
106//! let mut file1 = NamedTempFile::new()?;
107//!
108//! // Re-open it.
109//! let mut file2 = file1.reopen()?;
110//!
111//! // Write some test data to the first handle.
112//! file1.write_all(text.as_bytes())?;
113//!
114//! // Read the test data using the second handle.
115//! let mut buf = String::new();
116//! file2.read_to_string(&mut buf)?;
117//! assert_eq!(buf, text);
118//! # Ok(())
119//! # }
120//! ```
121//!
122//! Create a temporary directory and add a file to it:
123//!
124//! ```
125//! use tempfile::tempdir;
126//! use std::fs::File;
127//! use std::io::{self, Write};
128//!
129//! # fn main() {
130//! #     if let Err(_) = run() {
131//! #         ::std::process::exit(1);
132//! #     }
133//! # }
134//! # fn run() -> Result<(), io::Error> {
135//! // Create a directory inside of `std::env::temp_dir()`.
136//! let dir = tempdir()?;
137//!
138//! let file_path = dir.path().join("my-temporary-note.txt");
139//! let mut file = File::create(file_path)?;
140//! writeln!(file, "Brian was here. Briefly.")?;
141//!
142//! // By closing the `TempDir` explicitly, we can check that it has
143//! // been deleted successfully. If we don't close it explicitly,
144//! // the directory will still be deleted when `dir` goes out
145//! // of scope, but we won't know whether deleting the directory
146//! // succeeded.
147//! drop(file);
148//! dir.close()?;
149//! # Ok(())
150//! # }
151//! ```
152//!
153//! [`tempfile()`]: fn.tempfile.html
154//! [`tempdir()`]: fn.tempdir.html
155//! [`TempDir`]: struct.TempDir.html
156//! [`NamedTempFile`]: struct.NamedTempFile.html
157//! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
158//! [`lazy_static`]: https://github.com/rust-lang-nursery/lazy-static.rs/issues/62
159
160#![doc(
161    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
162    html_favicon_url = "https://www.rust-lang.org/favicon.ico",
163    html_root_url = "https://docs.rs/tempfile/3.1.0"
164)]
165#![cfg_attr(test, deny(warnings))]
166#![deny(rust_2018_idioms)]
167#![allow(clippy::redundant_field_names)]
168#![cfg_attr(all(feature = "nightly", target_os = "wasi"), feature(wasi_ext))]
169
170#[cfg(doctest)]
171doc_comment::doctest!("../README.md");
172
173const NUM_RETRIES: u32 = 1 << 31;
174const NUM_RAND_CHARS: usize = 6;
175
176use std::ffi::OsStr;
177use std::fs::OpenOptions;
178use std::path::Path;
179use std::{env, io};
180
181mod dir;
182mod error;
183mod file;
184mod spooled;
185mod util;
186
187pub use crate::dir::{tempdir, tempdir_in, TempDir};
188pub use crate::file::{
189    tempfile, tempfile_in, NamedTempFile, PathPersistError, PersistError, TempPath,
190};
191pub use crate::spooled::{spooled_tempfile, SpooledTempFile};
192
193/// Create a new temporary file or directory with custom parameters.
194#[derive(Debug, Clone, Eq, PartialEq)]
195pub struct Builder<'a, 'b> {
196    random_len: usize,
197    prefix: &'a OsStr,
198    suffix: &'b OsStr,
199    append: bool,
200}
201
202impl<'a, 'b> Default for Builder<'a, 'b> {
203    fn default() -> Self {
204        Builder {
205            random_len: crate::NUM_RAND_CHARS,
206            prefix: OsStr::new(".tmp"),
207            suffix: OsStr::new(""),
208            append: false,
209        }
210    }
211}
212
213impl<'a, 'b> Builder<'a, 'b> {
214    /// Create a new `Builder`.
215    ///
216    /// # Examples
217    ///
218    /// Create a named temporary file and write some data into it:
219    ///
220    /// ```
221    /// # use std::io;
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 named_tempfile = Builder::new()
232    ///     .prefix("my-temporary-note")
233    ///     .suffix(".txt")
234    ///     .rand_bytes(5)
235    ///     .tempfile()?;
236    ///
237    /// let name = named_tempfile
238    ///     .path()
239    ///     .file_name().and_then(OsStr::to_str);
240    ///
241    /// if let Some(name) = name {
242    ///     assert!(name.starts_with("my-temporary-note"));
243    ///     assert!(name.ends_with(".txt"));
244    ///     assert_eq!(name.len(), "my-temporary-note.txt".len() + 5);
245    /// }
246    /// # Ok(())
247    /// # }
248    /// ```
249    ///
250    /// Create a temporary directory and add a file to it:
251    ///
252    /// ```
253    /// # use std::io::{self, Write};
254    /// # use std::fs::File;
255    /// # use std::ffi::OsStr;
256    /// # fn main() {
257    /// #     if let Err(_) = run() {
258    /// #         ::std::process::exit(1);
259    /// #     }
260    /// # }
261    /// # fn run() -> Result<(), io::Error> {
262    /// use tempfile::Builder;
263    ///
264    /// let dir = Builder::new()
265    ///     .prefix("my-temporary-dir")
266    ///     .rand_bytes(5)
267    ///     .tempdir()?;
268    ///
269    /// let file_path = dir.path().join("my-temporary-note.txt");
270    /// let mut file = File::create(file_path)?;
271    /// writeln!(file, "Brian was here. Briefly.")?;
272    ///
273    /// // By closing the `TempDir` explicitly, we can check that it has
274    /// // been deleted successfully. If we don't close it explicitly,
275    /// // the directory will still be deleted when `dir` goes out
276    /// // of scope, but we won't know whether deleting the directory
277    /// // succeeded.
278    /// drop(file);
279    /// dir.close()?;
280    /// # Ok(())
281    /// # }
282    /// ```
283    ///
284    /// Create a temporary directory with a chosen prefix under a chosen folder:
285    ///
286    /// ```ignore
287    /// let dir = Builder::new()
288    ///     .prefix("my-temporary-dir")
289    ///     .tempdir_in("folder-with-tempdirs")?;
290    /// ```
291    #[must_use]
292    pub fn new() -> Self {
293        Self::default()
294    }
295
296    /// Set a custom filename prefix.
297    ///
298    /// Path separators are legal but not advisable.
299    /// Default: `.tmp`.
300    ///
301    /// # Examples
302    ///
303    /// ```
304    /// # use std::io;
305    /// # fn main() {
306    /// #     if let Err(_) = run() {
307    /// #         ::std::process::exit(1);
308    /// #     }
309    /// # }
310    /// # fn run() -> Result<(), io::Error> {
311    /// # use tempfile::Builder;
312    /// let named_tempfile = Builder::new()
313    ///     .prefix("my-temporary-note")
314    ///     .tempfile()?;
315    /// # Ok(())
316    /// # }
317    /// ```
318    pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self {
319        self.prefix = prefix.as_ref();
320        self
321    }
322
323    /// Set a custom filename suffix.
324    ///
325    /// Path separators are legal but not advisable.
326    /// Default: empty.
327    ///
328    /// # Examples
329    ///
330    /// ```
331    /// # use std::io;
332    /// # fn main() {
333    /// #     if let Err(_) = run() {
334    /// #         ::std::process::exit(1);
335    /// #     }
336    /// # }
337    /// # fn run() -> Result<(), io::Error> {
338    /// # use tempfile::Builder;
339    /// let named_tempfile = Builder::new()
340    ///     .suffix(".txt")
341    ///     .tempfile()?;
342    /// # Ok(())
343    /// # }
344    /// ```
345    pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self {
346        self.suffix = suffix.as_ref();
347        self
348    }
349
350    /// Set the number of random bytes.
351    ///
352    /// Default: `6`.
353    ///
354    /// # Examples
355    ///
356    /// ```
357    /// # use std::io;
358    /// # fn main() {
359    /// #     if let Err(_) = run() {
360    /// #         ::std::process::exit(1);
361    /// #     }
362    /// # }
363    /// # fn run() -> Result<(), io::Error> {
364    /// # use tempfile::Builder;
365    /// let named_tempfile = Builder::new()
366    ///     .rand_bytes(5)
367    ///     .tempfile()?;
368    /// # Ok(())
369    /// # }
370    /// ```
371    pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
372        self.random_len = rand;
373        self
374    }
375
376    /// Set the file to be opened in append mode.
377    ///
378    /// Default: `false`.
379    ///
380    /// # Examples
381    ///
382    /// ```
383    /// # use std::io;
384    /// # fn main() {
385    /// #     if let Err(_) = run() {
386    /// #         ::std::process::exit(1);
387    /// #     }
388    /// # }
389    /// # fn run() -> Result<(), io::Error> {
390    /// # use tempfile::Builder;
391    /// let named_tempfile = Builder::new()
392    ///     .append(true)
393    ///     .tempfile()?;
394    /// # Ok(())
395    /// # }
396    /// ```
397    pub fn append(&mut self, append: bool) -> &mut Self {
398        self.append = append;
399        self
400    }
401
402    /// Create the named temporary file.
403    ///
404    /// # Security
405    ///
406    /// See [the security][security] docs on `NamedTempFile`.
407    ///
408    /// # Resource leaking
409    ///
410    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
411    ///
412    /// # Errors
413    ///
414    /// If the file cannot be created, `Err` is returned.
415    ///
416    /// # Examples
417    ///
418    /// ```
419    /// # use std::io;
420    /// # fn main() {
421    /// #     if let Err(_) = run() {
422    /// #         ::std::process::exit(1);
423    /// #     }
424    /// # }
425    /// # fn run() -> Result<(), io::Error> {
426    /// # use tempfile::Builder;
427    /// let tempfile = Builder::new().tempfile()?;
428    /// # Ok(())
429    /// # }
430    /// ```
431    ///
432    /// [security]: struct.NamedTempFile.html#security
433    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
434    pub fn tempfile(&self) -> io::Result<NamedTempFile> {
435        self.tempfile_in(env::temp_dir())
436    }
437
438    /// Create the named temporary file in the specified directory.
439    ///
440    /// # Security
441    ///
442    /// See [the security][security] docs on `NamedTempFile`.
443    ///
444    /// # Resource leaking
445    ///
446    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
447    ///
448    /// # Errors
449    ///
450    /// If the file cannot be created, `Err` is returned.
451    ///
452    /// # Examples
453    ///
454    /// ```
455    /// # use std::io;
456    /// # fn main() {
457    /// #     if let Err(_) = run() {
458    /// #         ::std::process::exit(1);
459    /// #     }
460    /// # }
461    /// # fn run() -> Result<(), io::Error> {
462    /// # use tempfile::Builder;
463    /// let tempfile = Builder::new().tempfile_in("./")?;
464    /// # Ok(())
465    /// # }
466    /// ```
467    ///
468    /// [security]: struct.NamedTempFile.html#security
469    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
470    pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
471        util::create_helper(
472            dir.as_ref(),
473            self.prefix,
474            self.suffix,
475            self.random_len,
476            |path| file::create_named(path, OpenOptions::new().append(self.append)),
477        )
478    }
479
480    /// Attempts to make a temporary directory inside of `env::temp_dir()` whose
481    /// name will have the prefix, `prefix`. The directory and
482    /// everything inside it will be automatically deleted once the
483    /// returned `TempDir` is destroyed.
484    ///
485    /// # Resource leaking
486    ///
487    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
488    ///
489    /// # Errors
490    ///
491    /// If the directory can not be created, `Err` is returned.
492    ///
493    /// # Examples
494    ///
495    /// ```
496    /// use std::fs::File;
497    /// use std::io::Write;
498    /// use tempfile::Builder;
499    ///
500    /// # use std::io;
501    /// # fn run() -> Result<(), io::Error> {
502    /// let tmp_dir = Builder::new().tempdir()?;
503    /// # Ok(())
504    /// # }
505    /// ```
506    ///
507    /// [resource-leaking]: struct.TempDir.html#resource-leaking
508    pub fn tempdir(&self) -> io::Result<TempDir> {
509        self.tempdir_in(env::temp_dir())
510    }
511
512    /// Attempts to make a temporary directory inside of `dir`.
513    /// The directory and everything inside it will be automatically
514    /// deleted once the returned `TempDir` is destroyed.
515    ///
516    /// # Resource leaking
517    ///
518    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
519    ///
520    /// # Errors
521    ///
522    /// If the directory can not be created, `Err` is returned.
523    ///
524    /// # Examples
525    ///
526    /// ```
527    /// use std::fs::{self, File};
528    /// use std::io::Write;
529    /// use tempfile::Builder;
530    ///
531    /// # use std::io;
532    /// # fn run() -> Result<(), io::Error> {
533    /// let tmp_dir = Builder::new().tempdir_in("./")?;
534    /// # Ok(())
535    /// # }
536    /// ```
537    ///
538    /// [resource-leaking]: struct.TempDir.html#resource-leaking
539    pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
540        let storage;
541        let mut dir = dir.as_ref();
542        if !dir.is_absolute() {
543            let cur_dir = env::current_dir()?;
544            storage = cur_dir.join(dir);
545            dir = &storage;
546        }
547
548        util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create)
549    }
550
551    /// Attempts to create a temporary file (or file-like object) using the
552    /// provided closure. The closure is passed a temporary file path and
553    /// returns an [`std::io::Result`]. The path provided to the closure will be
554    /// inside of [`std::env::temp_dir()`]. Use [`Builder::make_in`] to provide
555    /// a custom temporary directory. If the closure returns one of the
556    /// following errors, then another randomized file path is tried:
557    ///  - [`std::io::ErrorKind::AlreadyExists`]
558    ///  - [`std::io::ErrorKind::AddrInUse`]
559    ///
560    /// This can be helpful for taking full control over the file creation, but
561    /// leaving the temporary file path construction up to the library. This
562    /// also enables creating a temporary UNIX domain socket, since it is not
563    /// possible to bind to a socket that already exists.
564    ///
565    /// Note that [`Builder::append`] is ignored when using [`Builder::make`].
566    ///
567    /// # Security
568    ///
569    /// This has the same [security implications][security] as
570    /// [`NamedTempFile`], but with additional caveats. Specifically, it is up
571    /// to the closure to ensure that the file does not exist and that such a
572    /// check is *atomic*. Otherwise, a [time-of-check to time-of-use
573    /// bug][TOCTOU] could be introduced.
574    ///
575    /// For example, the following is **not** secure:
576    ///
577    /// ```
578    /// # use std::io;
579    /// # use std::fs::File;
580    /// # fn main() {
581    /// #     if let Err(_) = run() {
582    /// #         ::std::process::exit(1);
583    /// #     }
584    /// # }
585    /// # fn run() -> Result<(), io::Error> {
586    /// # use tempfile::Builder;
587    /// // This is NOT secure!
588    /// let tempfile = Builder::new().make(|path| {
589    ///     if path.is_file() {
590    ///         return Err(io::ErrorKind::AlreadyExists.into());
591    ///     }
592    ///
593    ///     // Between the check above and the usage below, an attacker could
594    ///     // have replaced `path` with another file, which would get truncated
595    ///     // by `File::create`.
596    ///
597    ///     File::create(path)
598    /// })?;
599    /// # Ok(())
600    /// # }
601    /// ```
602    /// Note that simply using [`std::fs::File::create`] alone is not correct
603    /// because it does not fail if the file already exists:
604    /// ```
605    /// # use std::io;
606    /// # use std::fs::File;
607    /// # fn main() {
608    /// #     if let Err(_) = run() {
609    /// #         ::std::process::exit(1);
610    /// #     }
611    /// # }
612    /// # fn run() -> Result<(), io::Error> {
613    /// # use tempfile::Builder;
614    /// // This could overwrite an existing file!
615    /// let tempfile = Builder::new().make(|path| File::create(path))?;
616    /// # Ok(())
617    /// # }
618    /// ```
619    /// For creating regular temporary files, use [`Builder::tempfile`] instead
620    /// to avoid these problems. This function is meant to enable more exotic
621    /// use-cases.
622    ///
623    /// # Resource leaking
624    ///
625    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
626    ///
627    /// # Errors
628    ///
629    /// If the closure returns any error besides
630    /// [`std::io::ErrorKind::AlreadyExists`] or
631    /// [`std::io::ErrorKind::AddrInUse`], then `Err` is returned.
632    ///
633    /// # Examples
634    /// ```
635    /// # use std::io;
636    /// # fn main() {
637    /// #     if let Err(_) = run() {
638    /// #         ::std::process::exit(1);
639    /// #     }
640    /// # }
641    /// # fn run() -> Result<(), io::Error> {
642    /// # use tempfile::Builder;
643    /// # #[cfg(unix)]
644    /// use std::os::unix::net::UnixListener;
645    /// # #[cfg(unix)]
646    /// let tempsock = Builder::new().make(|path| UnixListener::bind(path))?;
647    /// # Ok(())
648    /// # }
649    /// ```
650    ///
651    /// [TOCTOU]: https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use
652    /// [security]: struct.NamedTempFile.html#security
653    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
654    pub fn make<F, R>(&self, f: F) -> io::Result<NamedTempFile<R>>
655    where
656        F: FnMut(&Path) -> io::Result<R>,
657    {
658        self.make_in(env::temp_dir(), f)
659    }
660
661    /// This is the same as [`Builder::make`], except `dir` is used as the base
662    /// directory for the temporary file path.
663    ///
664    /// See [`Builder::make`] for more details and security implications.
665    ///
666    /// # Examples
667    /// ```
668    /// # use std::io;
669    /// # fn main() {
670    /// #     if let Err(_) = run() {
671    /// #         ::std::process::exit(1);
672    /// #     }
673    /// # }
674    /// # fn run() -> Result<(), io::Error> {
675    /// # use tempfile::Builder;
676    /// # #[cfg(unix)]
677    /// use std::os::unix::net::UnixListener;
678    /// # #[cfg(unix)]
679    /// let tempsock = Builder::new().make_in("./", |path| UnixListener::bind(path))?;
680    /// # Ok(())
681    /// # }
682    /// ```
683    pub fn make_in<F, R, P>(&self, dir: P, mut f: F) -> io::Result<NamedTempFile<R>>
684    where
685        F: FnMut(&Path) -> io::Result<R>,
686        P: AsRef<Path>,
687    {
688        util::create_helper(
689            dir.as_ref(),
690            self.prefix,
691            self.suffix,
692            self.random_len,
693            move |path| {
694                Ok(NamedTempFile::from_parts(
695                    f(&path)?,
696                    TempPath::from_path(path),
697                ))
698            },
699        )
700    }
701}