tempfile

repository·master·Indexed 23 days ago

https://github.com/stebalien/tempfile

A secure, cross-platform Rust library for managing temporary files and directories. It provides utilities for creating unnamed temporary files, named temporary files via NamedTempFile, and temporary directories via TempDir. The library supports a wide range of platforms including Linux, Windows, macOS, BSDs, RedoxOS, and WASI. Key features include the ability to persist temporary files to permanent paths, spooled temporary files that back in-memory buffers to disk, and a Builder for fine-grained configuration of prefixes, suffixes, and permissions.

Tokens
5.9K
Snippets
18
Records
28
Agent score
80%

What's inside tempfile

  1. Override the temporary directory for WASI or Android

    master

    On certain platforms, you must explicitly define the temporary directory to avoid panics or to ensure files are stored in the correct location:

    • WASI P1/P2: Does not define a default temporary directory. You must call tempfile::env::override_temp_dir with a valid directory, otherwise temporary file creation will panic.
    • Android: You may need to override the temporary directory to point to your application's per-app cache directory.

    Use tempfile::env::override_temp_dir to set the path.

  2. Untitled record

    master

    SpooledTempFile is a temporary file that stays in memory until it reaches a specified max_size, at which point it is automatically "rolled over" to a temporary file on disk. This is useful for handling small amounts of data efficiently in memory while still providing a fallback for larger datasets that would otherwise consume excessive RAM.

    Key Behaviors

    • Automatic Rollover: When a write operation would cause the data to exceed max_size, the file is moved to disk.
    • Storage Location: By default, it uses the OS temporary directory. You can specify a custom directory using spooled_tempfile_in to ensure the file is backed by a specific filesystem (e.g., a persistent disk instead of an in-memory /tmp).
    • Cleanup: The underlying temporary file is automatically removed by the OS when the last handle is closed, providing reliable cleanup even if Rust destructors are not run.
    • Manual Rollover: You can force the file to move to disk immediately using .roll().
    use tempfile::spooled_tempfile;
    use std::io::Write;
    
    let mut file = spooled_tempfile(15);
    
    writeln!(file, "short line")?;
    assert!(!file.is_rolled());
    
    // This write exceeds max_size (15), triggering a rollover to disk
    writeln!(file, "marvin gardens")?;
    assert!(file.is_rolled());
  3. Manage named temporary files with `NamedTempFile`

    master

    A NamedTempFile provides a file that has a visible name on the filesystem. This is useful when a child process or external tool needs to access the file by its path.

    Security Warning: Using named files can be insecure on some platforms if a temporary file cleaner unlinks the file and an attacker replaces it with a different file before you re-open it. For maximum security, use tempfile() (unnamed) unless a name is strictly required.

    use tempfile::NamedTempFile;
    use std::io::Write;
    
    let mut file = NamedTempFile::new()?;
    writeln!(file, "Brian was here. Briefly.")?;
  4. Manage temporary directories with `TempDir`

    master

    The TempDir struct represents a directory on the filesystem that is automatically deleted when it goes out of scope.

    Key Methods

    • path(): Returns a reference to the Path of the temporary directory.
    • keep(): Consumes the TempDir and returns the PathBuf, preventing automatic deletion. Use this to persist the directory to disk.
    • close(): Explicitly closes and removes the directory, returning a Result. This is preferred over relying on the destructor if you need to handle potential errors during cleanup.
    • disable_cleanup(bool): Disables automatic deletion when the object is dropped. This is primarily useful for debugging.

    Important Considerations

    • Resource Leaking: If the program exits abruptly (e.g., via std::process::exit(), a segfault, or receiving a signal like SIGINT), the directory will not be deleted.
    • Cleanup Errors: The Drop implementation for TempDir silently ignores errors during deletion. To catch errors during cleanup, call .close() explicitly.
    • File Handles: Ensure all file handles (like File or ReadDir) pointing to files inside the directory are dropped before the TempDir is dropped to avoid cleanup failures on some platforms.
    use std::fs;
    use tempfile::TempDir;
    
    let tmp_dir = TempDir::new()?;
    
    // Persist the temporary directory to disk, getting the path where it is.
    let tmp_path = tmp_dir.keep();
    
    // Delete the temporary directory ourselves.
    fs::remove_dir_all(tmp_path)?;
  5. Avoid the early drop pitfall with `TempDir` and `NamedTempFile`

    master

    Because TempDir and NamedTempFile rely on destructors for cleanup, passing them into functions that take AsRef<Path> can cause them to be dropped prematurely if the function consumes the value.

    Incorrect: Moving the object into a function that consumes it. Correct: Passing a reference to the object so the destructor runs only after the function completes.

    use tempfile::tempdir;
    use std::process::Command;
    
    // Create a directory inside of `env::temp_dir()`.
    let temp_dir = tempdir()?;
    
    // Spawn the `touch` command inside the temporary directory and collect the exit status
    // Note that `temp_dir` is **not** moved into `current_dir`, but passed as a reference
    let exit_status = Command::new("touch").arg("tmp").current_dir(&temp_dir).status()?;
    assert!(exit_status.success());
    
    # Ok::<(), std::io::Error>(())
  6. Security considerations for temporary files

    master

    When using tempfile, consider the following security aspects:

    Access Permissions

    • Files: Created with private permissions by default on all OSs.
    • Directories: Created with default system permissions and may be world-readable unless the user's umask or the default temporary directory is configured otherwise.

    Denial of Service

    If rand_bytes is too small or the getrandom feature is missing, attackers might predict filenames. The library mitigates this by defaulting to 6 random characters and re-seeding the generator after failed attempts (if getrandom is enabled).

    Temporary File Cleaners (Unix-like systems)

    On Unix, system cleaners might delete files that haven't been accessed recently. This can invalidate paths for NamedTempFile and TempDir. To mitigate this, avoid relying on file paths for long-lived files or place them in directories not managed by cleaners.

  7. Create and use a temporary file

    master

    Use tempfile::tempfile() to create a new temporary file. The file is automatically deleted when the file handle is closed (depending on the OS and implementation details).

    use std::fs::File;
    use std::io::{Write, Read, Seek, SeekFrom};
    
    fn main() {
        // Write
        let mut tmpfile: File = tempfile::tempfile().unwrap();
        write!(tmpfile, "Hello World!").unwrap();
    
        // Seek to start
        tmpfile.seek(SeekFrom::Start(0)).unwrap();
    
        // Read
        let mut buf = String::new();
        tmpfile.read_to_string(&mut buf).unwrap();
        assert_eq!("Hello World!", buf);
    }
  8. Supported Platforms

    master

    The tempfile crate supports a wide range of operating systems:

    • Linux: Android, Linux
    • BSDs: DragonFly BSD, FreeBSD, MidnightBSD, NetBSD, OpenBSD
    • Illumos: OpenIndiana, OmniOS
    • MacOS: ios, watchos, visionos, etc.
    • RedoxOS
    • Wasm: WASI P1 & P2, Wasm (build and link only)
    • Windows

    Platform-specific Notes

    • Android, RedoxOS, Wasm, and WASI require the latest stable Rust compiler.
    • WASI P1/P2 does not define a default temporary directory and does not have file permissions.
    • BSD/Illumos targets are not tested for compatibility with older Rust compilers.
  9. Create a temporary directory in a specific location with `tempdir_in()`

    master

    Use tempfile::tempdir_in(dir) to create a new temporary directory inside a specific existing directory. The directory will be automatically deleted when the TempDir is dropped.

    Returns an io::Result<TempDir>.

    use tempfile::tempdir_in;
    use std::fs::File;
    use std::io::Write;
    
    // Create a directory inside of the current directory.
    let tmp_dir = tempdir_in(".")?;
    
    let file_path = tmp_dir.path().join("my-temporary-note.txt");
    let mut tmp_file = File::create(file_path)?;
    writeln!(tmp_file, "Brian was here. Briefly.")?;
    
    // `tmp_dir` goes out of scope, the directory as well as
    // `tmp_file` will be deleted here.
    drop(tmp_file);
    tmp_dir.close()?;
    # Ok::<(), std::io::Error>(())
  10. Securely reopen a `NamedTempFile`

    master

    Use reopen() to get a new file handle to the same file that a NamedTempFile manages. This is more secure than File::open(path) because it guarantees you are opening the exact same file, even if a temporary file cleaner has unlinked the original path.

    use tempfile::NamedTempFile;
    
    let file = NamedTempFile::new()?;
    let another_handle = file.reopen()?;
  11. Create a spooled temporary file in a specific directory with `spooled_tempfile_in`

    master

    Constructs a SpooledTempFile that will be backed by a file in the specified directory once it rolls over to disk. This is recommended if your default OS temporary directory is an in-memory filesystem and you require disk-backed storage.

    Note: The specified path is not checked until the file is actually rolled over to disk. If the directory is not writable, writes will fail once max_size is reached.

    use tempfile::spooled_tempfile_in;
    use std::path::Path;
    
    let mut file = spooled_tempfile_in(1024, "/var/tmp");