partclone: a failed status file must not end the restore - #185
Merged
Merged
Conversation
fogLogStatusFile() is FOG's own addition to partclone's progress.c. It is the only writer of /tmp/status.fog, which fog.statusreporter forwards to service/progress.php. It called exit(0) when it could not open that file. exit(0) is a SUCCESS status. funcs.sh reads $exitcode after the partclone pipeline and only calls handleError when it is non-zero, so partclone quitting halfway through a restore is recorded as a good deploy: the task completes, the client reboots, and the partition is partially written with nothing said. /tmp in FOS is a ramdisk, so a full one is enough to reach it. Progress reporting is best effort; it now warns once on stderr and returns, and the restore continues with a stalled percentage on the web UI. Two defects in the same function, fixed while editing it: sprintf(total_str, filesize_conv(totalsize, buf, max_len)) passes the converted string as sprintf's FORMAT argument, so a % in it would read an argument that was never passed. filesize_conv() already writes into the buffer it is handed, so it now writes into total_str directly and the shared scratch buffer is gone -- which also removes the aliasing between the two calls. max_len was a variable sizing two VLAs for a constant 15 bytes. The buffers are char[16] and sizeof() is passed to filesize_conv(). tests/checks/partclone-status-patch.sh gates the patch as text: no exit() in the function, a failed open returns, no sprintf format that is a call's return value, and every hunk header's counts match the lines under it. The last one is not incidental -- the added code is `+` lines inside a unified diff, so editing it by hand moves counts that nothing else recomputes, and `patch` will often apply a wrong count and silently drop or duplicate a line in the middle of a Buildroot build. Verified: the patch applies with no fuzz or offset against pristine partclone-0.3.47, and `make partclone` re-extracts, re-patches, and rebuilds it to a linked partclone.restore. Each of the four assertions was driven red by restoring the defect it covers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The defect
fogLogStatusFile()is FOG's own addition to partclone'sprogress.c(
Buildroot/package/partclone/partclone-0.3.47.patch). It is the only writer of/tmp/status.fog, whichfog.statusreporterforwards toservice/progress.php.It called
exit(0)when it could not open that file.exit(0)is a SUCCESS status.funcs.shreads$exitcodeafter the partclonepipeline and calls
handleErroronly when it is non-zero. So partclone quittinghalfway through a restore is recorded as a good deploy: the task completes, the
client reboots, and the partition is partially written with nothing said on
screen or in the log.
/tmpin FOS is a ramdisk, so a full one is enough toreach it.
Progress reporting is best effort. It now warns once on stderr and returns. The
restore continues, and the cost is a stalled percentage on the web UI.
Two more defects in the same function
Both were in front of me while editing it.
sprintf(total_str, filesize_conv(totalsize, buf, max_len))sprintf's FORMAT argument, so a%in it reads an argument that was never passedfilesize_conv(totalsize, total_str, sizeof(total_str))— it already writes into the buffer it is handedint max_len=15;sizing two VLAs, plus a sharedbufchar total_str[16];/char current_str[16];, no shared bufferThe gate
tests/checks/partclone-status-patch.shchecks the patch as text:exit(anywhere infogLogStatusFile()fopenreturns (so removing theexitwithout replacing it cannot pass)sprintfwhose format argument is a call's return value@@header's counts match the lines under itThe last one is not incidental. The added code is
+lines inside a unifieddiff, so editing it by hand moves counts that nothing else recomputes.
patchwill often apply a wrong count anyway and silently drop or duplicate a line; the
failure then lands in the middle of a Buildroot build, far from the edit.
Verification
partclone-0.3.47(
patch -p1 --dry-run).make partclone-dirclean && make partcloneinfssourcex64re-extracts,re-patches, re-configures and rebuilds it to a linked
partclone.restore. Thefunction in the extracted tree is the new one.
exit(0)back (2 red), thereturndeleted (2 red), the computedsprintfformat back (1 red), a stale hunk count (1 red).
tests/run-all.sh: 24 passed, 0 failed.Verification gap: this has not been run against a real deploy with a full
/tmp. The path that was changed is the failure path, which is exactly the one anormal lab run never takes. What is proven is that the patched source compiles
and that the success path is byte-identical in behavior to before.
FOS is not branched, so this init runs against 1.5 and 1.6 servers alike. The
wire format written to
/tmp/status.fogis unchanged.🤖 Generated with Claude Code