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