blob: d89798f509f6d5c62e291b51672a2194146d7fcb [file] [log] [blame]
Jiyong Park7ce5f522021-11-01 09:47:19 +09001//! 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]
131extern crate cfg_if;
132
133#[cfg(doctest)]
134#[macro_use]
135extern crate doc_comment;
136
137#[cfg(doctest)]
138doctest!("../README.md");
139
140const NUM_RETRIES: u32 = 1 << 31;
141const NUM_RAND_CHARS: usize = 6;
142
143use std::ffi::OsStr;
144use std::fs::OpenOptions;
145use std::path::Path;
146use std::{env, io};
147
148mod dir;
149mod error;
150mod file;
151mod spooled;
152mod util;
153
154pub use crate::dir::{tempdir, tempdir_in, TempDir};
155pub use crate::file::{
156 tempfile, tempfile_in, NamedTempFile, PathPersistError, PersistError, TempPath,
157};
158pub use crate::spooled::{spooled_tempfile, SpooledTempFile};
159
160/// Create a new temporary file or directory with custom parameters.
161#[derive(Debug, Clone, Eq, PartialEq)]
162pub struct Builder<'a, 'b> {
163 random_len: usize,
164 prefix: &'a OsStr,
165 suffix: &'b OsStr,
166 append: bool,
167}
168
169impl<'a, 'b> Default for Builder<'a, 'b> {
170 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
180impl<'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 /// ```
250 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 /// ```
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 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 /// ```
303 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 /// ```
329 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 /// ```
355 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
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
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
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
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}