parcopy
Parallel, atomic, and safe file/directory copying for Rust.
A production-grade library for copying files and directories with safety guarantees that go beyond the standard library.
Features
- Parallel copying - Uses rayon for concurrent file operations
- Atomic writes - Uses temp file + rename pattern to ensure no partial files
- TOCTOU safe - Uses
persist_noclobberto prevent race conditions - Incremental copy - Only copy files newer than destination (
UpdateNewer) - Reflink support - Instant copy-on-write on btrfs/XFS/APFS
- Timestamp preserving - Copies file modification and access times
- Permission preserving - Copies file and directory permissions
- Symlink aware - Correctly handles symlinks without following them
- Symlink loop detection - Prevents infinite recursion from circular symlinks
- Security hardened - Detects and optionally blocks escaping symlinks
Why parcopy?
| Feature | std::fs |
fs_extra |
parcopy |
|---|---|---|---|
| Parallel | ❌ | ❌ | ✅ |
| Atomic writes | ❌ | ❌ | ✅ |
| TOCTOU safe | ❌ | ❌ | ✅ |
| Incremental copy | ❌ | ❌ | ✅ |
| Reflink/CoW | ❌ | ❌ | ✅ |
| Timestamp preservation | ❌ | ❌ | ✅ |
| Progress callbacks | ❌ | ✅ | ✅ |
Installation
[]
= "0.1"
Optional Features
[]
= { = "0.1", = ["progress", "reflink"] }
| Feature | Description |
|---|---|
progress |
Progress bar support with indicatif |
reflink |
Copy-on-write support for btrfs/XFS/APFS |
tracing |
Structured logging with tracing crate |
serde |
Serialize/Deserialize for CopyOptions |
full |
Enable all optional features |
Quick Start
Builder API (Recommended)
The easiest way to use parcopy is with the CopyBuilder:
use CopyBuilder;
// Simple copy with smart defaults
let stats = new.run?;
println!;
Incremental Backup
Only copy files that have changed:
use CopyBuilder;
let stats = new
.update_newer
.run?;
println!;
High-Performance Copy
Optimize for NFS or network filesystems:
use CopyBuilder;
let stats = new
.parallel // More threads for NFS
.overwrite // Replace existing files
.no_fsync // Skip fsync for speed
.run?;
Security-Hardened Copy
Copy untrusted directories safely:
use CopyBuilder;
let stats = new
.block_escaping_symlinks // Block symlinks with "../"
.max_depth // Limit directory depth
.run?;
Function API
For more control, use the function API with CopyOptions:
use ;
use Path;
let options = default
.with_parallel
.with_on_conflict
.with_max_depth
.without_fsync;
let stats = copy_dir?;
Configuration Options
| Option | Default | Description |
|---|---|---|
parallel |
16 | Number of concurrent copy operations |
on_conflict |
Skip |
How to handle existing files |
fsync |
true |
Sync data to disk after each file |
preserve_permissions |
true |
Copy file permissions |
preserve_timestamps |
true |
Copy file timestamps |
max_depth |
None |
Maximum directory depth |
block_escaping_symlinks |
false |
Block symlinks with .. |
Conflict Strategies
| Strategy | Description |
|---|---|
OnConflict::Skip |
Skip files that already exist (default) |
OnConflict::Overwrite |
Replace existing files |
OnConflict::UpdateNewer |
Only copy if source is newer |
OnConflict::Error |
Return error if file exists |
Copy Statistics
All copy operations return CopyStats:
use CopyBuilder;
let stats = new.run?;
println!;
println!;
println!;
println!;
println!;
println!;
Safety Guarantees
Atomic Writes
Files are written to a temporary file in the destination directory, then renamed atomically:
- No partial files - Interrupted copies leave no garbage
- All-or-nothing - Other processes see complete files or nothing
- Power failure safe - With
fsync: true, data survives crashes
TOCTOU Protection
Uses renameat2(RENAME_NOREPLACE) on Linux to atomically fail if the destination was created between our existence check and the rename.
Symlink Safety
- Symlinks are never followed during directory traversal
- Symlink loops are detected and reported
- Escaping symlinks (
../) are warned or blocked
Performance Notes
NFS Optimization
This crate is optimized for NFS and network filesystems where many small files cause metadata storms. By parallelizing operations, multiple NFS RPCs can be in-flight simultaneously.
// For slow NFS, reduce parallelism to avoid overwhelming the server
let stats = new
.parallel
.run?;
Local SSD
For local SSDs, parallelism helps less but doesn't hurt:
// Default parallelism (16) works well for local storage too
let stats = new.run?;
Large Files
For large files, the reflink feature provides instant copy-on-write on supported filesystems (btrfs, XFS, APFS):
[]
= { = "0.1", = ["reflink"] }
CLI Tool
A CLI tool pcp is available in the cli directory:
# Install
# Usage
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.