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