All Resources
Code Snippet.NET2026-03-16

Safe File Copy, Move, and Directory Sync in C#

Robust file operations with retry logic, timestamp-based change detection, and conflict awareness. Patterns from a production PDM file sync tool.

csharp
public static class SafeFileOps
{
    /// <summary>Copy with exponential backoff retry.</summary>
    public static void CopyWithRetry(string src, string dest, int maxRetries = 3)
    {
        for (int attempt = 0; attempt <= maxRetries; attempt++)
        {
            try
            {
                File.Copy(src, dest, overwrite: true);
                return;
            }
            catch (IOException) when (attempt < maxRetries)
            {
                Thread.Sleep((int)Math.Pow(2, attempt) * 200);
            }
        }
    }

    /// <summary>Sync only changed files (2-second tolerance).</summary>
    public static int SyncDirectory(string sourceDir, string destDir)
    {
        int synced = 0;
        Directory.CreateDirectory(destDir);

        foreach (var srcFile in Directory.GetFiles(sourceDir, "*",
            SearchOption.AllDirectories))
        {
            var relativePath = Path.GetRelativePath(sourceDir, srcFile);
            var destFile = Path.Combine(destDir, relativePath);

            if (File.Exists(destFile))
            {
                var srcTime = File.GetLastWriteTime(srcFile);
                var destTime = File.GetLastWriteTime(destFile);

                // 2-second tolerance for filesystem precision
                if (Math.Abs((srcTime - destTime).TotalSeconds) < 2)
                    continue;
            }

            Directory.CreateDirectory(Path.GetDirectoryName(destFile)!);
            CopyWithRetry(srcFile, destFile);
            synced++;
        }

        return synced;
    }
}

How It Works

The CopyWithRetry method handles the most common file operation failure: another process holding a lock. Instead of failing immediately, it retries with exponential backoff (200ms, 400ms, 800ms). The when filter ensures only IOException triggers retry — other exceptions (permission denied, path not found) fail immediately.

The SyncDirectory method walks the source tree and only copies files that have actually changed, using a timestamp comparison with tolerance.

Why 2-Second Tolerance

File system timestamp precision varies dramatically:

File SystemPrecision
NTFS100 nanoseconds
FAT322 seconds
exFAT10 milliseconds
Network sharesVariable (often rounded to seconds)

When syncing between an NTFS local drive and a FAT32 USB or network share, a file copied at 10:30:00.500 might be stored as 10:30:00 on the destination. Without tolerance, the next sync would see a difference and re-copy everything. The 2-second window handles all common cases.

Integration Tips

  • Combine with FileSystemWatcher: Use FileChangeMonitor for real-time detection, then SyncDirectory for the actual copy. The watcher tells you *when* to sync; the sync method handles *what* to copy.
  • Avoid locks: Open source files with FileShare.Read to prevent blocking other processes:
using var stream = new FileStream(path,
    FileMode.Open, FileAccess.Read, FileShare.Read);
  • Long paths: If your paths might exceed 260 characters (common with nested SolidWorks assemblies), prefix with \\?\ or use .NET 8+ which handles long paths natively.
  • Read-only files: Check File.GetAttributes before overwriting — some files (library parts, toolbox components) are intentionally read-only.
C#.NETFile I/OSyncError Handling