How Croc Handles File Transfer Resume After Interruption
Croc implements resumable file transfers at the receiver side by detecting existing partial files, verifying their hashes, calculating missing chunks via utils.MissingChunks, and requesting only the remaining data from the sender.
Croc is a secure, peer-to-peer file transfer tool written in Go that enables seamless resumption of interrupted transfers without restarting from the beginning. Unlike simple append-only resume mechanisms, croc uses a chunk-oriented protocol that identifies exactly which portions of a file are missing and requests only those specific chunks. This article examines the resume implementation in the schollz/croc repository, focusing on the receiver-side logic in src/croc/croc.go and the chunk-level data management system.
Detecting Existing Files and Calculating Missing Chunks
When a transfer reconnects after interruption, croc first checks whether the destination file already exists. In src/croc/croc.go, the function updateIfRecipientHasFileInfo (lines 331–351) orchestrates this detection process.
The receiver follows a strict validation sequence:
- File existence check: Verifies if a file with the expected name exists at the destination path
- Size verification: Compares local file size against the sender's advertised size
- Hash validation: Computes the hash of the local file and compares it with the sender's hash
When hashes differ—indicating partial or corrupted data—croc calculates the exact missing segments:
// From src/utils/utils.go
chunkRanges := utils.MissingChunks(filename, chunkSize)
missingChunks := utils.ChunkRangesToChunks(chunkRanges)
The MissingChunks function scans the local file for empty (zero-filled) sections, while ChunkRangesToChunks converts these ranges into a flat list of specific chunk offsets. The chunk size is calculated as models.TCP_BUFFER_SIZE/2, optimizing the balance between network efficiency and resume granularity.
User Confirmation and Resume Negotiation
Unless the --overwrite flag is supplied, croc pauses to confirm the resume operation. The system outputs a prompt showing the percentage already received:
Resume 'myfile.txt' (87.3%)? (y/N) (use --overwrite to omit)
When the user confirms (or when --overwrite is used), the receiver constructs a resume request containing the list of missing chunk offsets. Using the communication layer in src/comm/comm.go—specifically Comm.Send and Comm.Receive—the receiver transmits this control message to the sender via the established secure channel.
The sender's sendData routine receives this information and continues streaming data for all chunks in sequence. However, the protocol design allows the sender to transmit the complete stream while the receiver selectively processes only the bytes belonging to the missing ranges.
Chunk-Level Data Writing and Offset Management
The actual data reconstruction occurs in receiveData within src/croc/croc.go. As the sender streams chunks, the receiver processes each one using CurrentFile.WriteAt:
// Writing at specific offset in src/croc/croc.go
file.WriteAt(chunkData, offset)
This random-access approach enables precise resume behavior:
- Selective writing: The receiver writes only chunks matching the missing list, discarding data for chunks that already exist locally
- Exact positioning: Each chunk is written to its specific byte offset rather than appending sequentially
- Completion detection: The transfer finishes when all expected chunks are received, signaled by
Message.TypeCloseSenderfromsrc/message/message.go
Progress Synchronization and UI Updates
To provide accurate visual feedback during a resumed transfer, croc updates the progress bar to reflect the already-received portion immediately. The code calls:
c.bar.Add64(bytesDone)
This fast-forwards the progress indicator to match the percentage of data already present on disk. For example, if 87.3% of the file exists locally, the progress bar begins at 87.3% rather than filling from zero.
Summary
- Detection logic:
updateIfRecipientHasFileInfoinsrc/croc/croc.go(lines 331–351) identifies existing files and validates integrity through hashing - Chunk calculation:
utils.MissingChunksandutils.ChunkRangesToChunksinsrc/utils/utils.godetermine exactly which data requires retransmission - Selective I/O: The receiver uses
CurrentFile.WriteAtto write only missing chunks at specific offsets while the sender'ssendDatastreams all chunks - User control: Resume requires confirmation unless
--overwriteis specified - Progress tracking:
c.bar.Add64(bytesDone)synchronizes the UI to show accurate resume progress
Frequently Asked Questions
How does croc determine which parts of a file need to be retransmitted?
Croc scans the existing partial file using utils.MissingChunks to detect empty (zero-filled) sections, then converts these ranges into specific chunk offsets with utils.ChunkRangesToChunks. This process, implemented in src/utils/utils.go, creates a precise inventory of missing data blocks that the receiver requests from the sender.
What happens if I move the partially downloaded file to a different location?
Croc only resumes transfers if the partial file exists at the original destination path with the matching filename. If the file is moved, croc will treat the transfer as new. Moving the file back to the original location before restarting will allow the resume detection in updateIfRecipientHasFileInfo to function correctly.
Does using --overwrite affect the resume capability?
Yes, the --overwrite flag forces croc to overwrite any existing file completely, starting the transfer from the beginning regardless of how much data was previously received. This bypasses the hash verification and missing chunk calculation entirely, effectively disabling the resume feature for that transfer.
Is the resume feature compatible with all versions of croc?
The resume mechanism relies on the chunk-oriented protocol and specific message types like Message.TypeCloseSender defined in src/message/message.go. Both sender and receiver must support the control message protocol for resume to function. The receiver-side implementation in src/croc/croc.go handles the detection and negotiation, making resume generally compatible when both parties use standard croc versions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →