Crate parcopy

Crate parcopy 

Source
Expand description

§parcopy

Parallel, atomic, and safe file/directory copying for Rust.

§Core Features

  • Parallel copying: Uses rayon for concurrent file operations, optimized for NFS/network storage
  • Atomic writes: Uses temp file + rename pattern to ensure no partial files
  • TOCTOU safe: Uses persist_noclobber to prevent race conditions
  • Symlink aware: Correctly handles symlinks without following them
  • Symlink loop detection: Prevents infinite recursion from circular symlinks
  • Resumable: Skips already-existing files for interrupted operations
  • Permission preserving: Copies file and directory permissions
  • Timestamp preserving: Copies file modification and access times
  • Incremental copy: Only copy files newer than destination (UpdateNewer)
  • Reflink support: Instant copy-on-write on btrfs/XFS/APFS
  • Security hardened: Detects and optionally blocks escaping symlinks

§Quick Start with Builder API

The easiest way to use parcopy is with the CopyBuilder:

use parcopy::CopyBuilder;

// Simple copy with smart defaults
let stats = CopyBuilder::new("src", "dst").run()?;
println!("Copied {} files ({} bytes)", stats.files_copied, stats.bytes_copied);

§Incremental Backup

use parcopy::CopyBuilder;

// Only copy files that have changed
let stats = CopyBuilder::new("project", "backup")
    .update_newer()
    .run()?;

println!("Updated {} files, {} already up-to-date",
    stats.files_copied, stats.files_skipped);

§High-Performance Copy

use parcopy::CopyBuilder;

let stats = CopyBuilder::new("data", "backup")
    .parallel(32)      // More threads for NFS
    .overwrite()       // Replace existing
    .no_fsync()        // Skip fsync for speed
    .run()?;

§Function API

For more control, use the function API with CopyOptions:

use parcopy::{copy_dir, CopyOptions, OnConflict};
use std::path::Path;

let options = CopyOptions::default()
    .with_parallel(8)                         // Limit parallelism
    .with_on_conflict(OnConflict::Overwrite)  // Overwrite existing
    .with_max_depth(100)                      // Limit recursion depth
    .with_block_escaping_symlinks()           // Block dangerous symlinks
    .without_fsync();                         // Faster but less durable

let stats = copy_dir(Path::new("src"), Path::new("dst"), &options)?;
println!("Copied {} files, skipped {}", stats.files_copied, stats.files_skipped);

§Safety Guarantees

§Atomic Writes

Files are written to a temporary file in the destination directory, then renamed atomically. This ensures no partial files exist if interrupted.

§TOCTOU Protection

Uses persist_noclobber (backed by renameat2(RENAME_NOREPLACE) on Linux) to atomically fail if destination was created between existence check and rename.

  • Symlinks are never followed during directory traversal
  • Symlink loops are detected and reported as Error::SymlinkLoop
  • Escaping symlinks (../) are warned or blocked based on configuration

§Optional Features

FeatureDescription
progressProgress bar support with indicatif
tracingStructured logging with tracing crate
serdeSerialize/Deserialize for CopyOptions
fullEnable all optional features

§NFS Optimization

This crate is specifically optimized for NFS and network filesystems where many small files cause metadata storms. By parallelizing file operations, we can have multiple NFS RPCs in-flight simultaneously, significantly improving throughput. Default parallelism is 16 concurrent operations.

Structs§

CopyBuilder
A builder for configuring and executing copy operations.
CopyOptions
Options for copy operations.
CopyStats
Statistics from a copy operation.

Enums§

Error
Errors that can occur during copy operations.
OnConflict
Behavior when destination file already exists.

Functions§

copy_dir
Copy a directory recursively with parallel file operations
copy_file
Copy a single file atomically
create_progress_barprogress
Create a default progress bar for file copying

Type Aliases§

ProgressCallbackprogress
Callback for progress updates
Result
Result type for parcopy operations.