• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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